Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull second set of media updates from Mauro Carvalho Chehab: - radio API: add support to work with radio frequency bands - new AM/FM radio drivers: radio-shark, radio-shark2 - new Remote Controller USB driver: iguanair - conversion of several drivers to the v4l2 core control framework - new board additions at existing drivers - the remaining (and vast majority of the patches) are due to drivers/DocBook fixes/cleanups. * 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (154 commits) [media] radio-tea5777: use library for 64bits div [media] tlg2300: Declare MODULE_FIRMWARE usage [media] lgs8gxx: Declare MODULE_FIRMWARE usage [media] xc5000: Add MODULE_FIRMWARE statements [media] s2255drv: Add MODULE_FIRMWARE statement [media] dib8000: move dereference after check for NULL [media] Documentation: Update cardlists [media] bttv: add support for Aposonic W-DVR [media] cx25821: Remove bad strcpy to read-only char* [media] pms.c: remove duplicated include [media] smiapp-core.c: remove duplicated include [media] via-camera: pass correct format settings to sensor [media] rtl2832.c: minor cleanup [media] Add support for the IguanaWorks USB IR Transceiver [media] Minor cleanups for MCE USB [media] drivers/media/dvb/siano/smscoreapi.c: use list_for_each_entry [media] Use a named union in struct v4l2_ioctl_info [media] mceusb: Add Twisted Melon USB IDs [media] staging/media/solo6x10: use module_pci_driver macro [media] staging/media/dt3155v4l: use module_pci_driver macro ... Conflicts: Documentation/feature-removal-schedule.txt
This commit is contained in:
Коммит
8762541f06
|
@ -2460,7 +2460,7 @@ that used it. It was originally scheduled for removal in 2.6.35.
|
|||
</section>
|
||||
|
||||
<section>
|
||||
<title>V4L2 in Linux 3.5</title>
|
||||
<title>V4L2 in Linux 3.6</title>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>Replaced <structfield>input</structfield> in
|
||||
|
@ -2471,6 +2471,24 @@ that used it. It was originally scheduled for removal in 2.6.35.
|
|||
</orderedlist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>V4L2 in Linux 3.6</title>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>Added V4L2_CAP_VIDEO_M2M and V4L2_CAP_VIDEO_M2M_MPLANE capabilities.</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>V4L2 in Linux 3.6</title>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>Added support for frequency band enumerations: &VIDIOC-ENUM-FREQ-BANDS;.</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</section>
|
||||
|
||||
<section id="other">
|
||||
<title>Relation of V4L2 to other Linux multimedia APIs</title>
|
||||
|
||||
|
@ -2600,6 +2618,9 @@ ioctls.</para>
|
|||
<para><link linkend="v4l2-auto-focus-area"><constant>
|
||||
V4L2_CID_AUTO_FOCUS_AREA</constant></link> control.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Support for frequency band enumeration: &VIDIOC-ENUM-FREQ-BANDS; ioctl.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -372,6 +372,11 @@ minimum value disables backlight compensation.</entry>
|
|||
Cr component, bits [15:8] as Cb component and bits [31:16] must be zero.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_CID_AUTOBRIGHTNESS</constant></entry>
|
||||
<entry>boolean</entry>
|
||||
<entry>Enable Automatic Brightness.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_CID_ROTATE</constant></entry>
|
||||
<entry>integer</entry>
|
||||
|
|
|
@ -140,6 +140,11 @@ structs, ioctls) must be noted in more detail in the history chapter
|
|||
applications. -->
|
||||
|
||||
<revision>
|
||||
<revnumber>3.6</revnumber>
|
||||
<date>2012-07-02</date>
|
||||
<authorinitials>hv</authorinitials>
|
||||
<revremark>Added VIDIOC_ENUM_FREQ_BANDS.
|
||||
</revremark>
|
||||
<revnumber>3.5</revnumber>
|
||||
<date>2012-05-07</date>
|
||||
<authorinitials>sa, sn</authorinitials>
|
||||
|
@ -534,6 +539,7 @@ and discussions on the V4L mailing list.</revremark>
|
|||
&sub-enum-fmt;
|
||||
&sub-enum-framesizes;
|
||||
&sub-enum-frameintervals;
|
||||
&sub-enum-freq-bands;
|
||||
&sub-enuminput;
|
||||
&sub-enumoutput;
|
||||
&sub-enumstd;
|
||||
|
|
|
@ -64,7 +64,7 @@ different sizes.</para>
|
|||
<para>To allocate device buffers applications initialize relevant fields of
|
||||
the <structname>v4l2_create_buffers</structname> structure. They set the
|
||||
<structfield>type</structfield> field in the
|
||||
<structname>v4l2_format</structname> structure, embedded in this
|
||||
&v4l2-format; structure, embedded in this
|
||||
structure, to the respective stream or buffer type.
|
||||
<structfield>count</structfield> must be set to the number of required buffers.
|
||||
<structfield>memory</structfield> specifies the required I/O method. The
|
||||
|
@ -114,7 +114,7 @@ information.</para>
|
|||
/></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>struct v4l2_format</entry>
|
||||
<entry>&v4l2-format;</entry>
|
||||
<entry><structfield>format</structfield></entry>
|
||||
<entry>Filled in by the application, preserved by the driver.</entry>
|
||||
</row>
|
||||
|
|
|
@ -54,15 +54,9 @@
|
|||
interface and may change in the future.</para>
|
||||
</note>
|
||||
|
||||
<para>To query the available timings, applications initialize the
|
||||
<structfield>index</structfield> field and zero the reserved array of &v4l2-dv-timings-cap;
|
||||
and call the <constant>VIDIOC_DV_TIMINGS_CAP</constant> ioctl with a pointer to this
|
||||
structure. Drivers fill the rest of the structure or return an
|
||||
&EINVAL; when the index is out of bounds. To enumerate all supported DV timings,
|
||||
applications shall begin at index zero, incrementing by one until the
|
||||
driver returns <errorcode>EINVAL</errorcode>. Note that drivers may enumerate a
|
||||
different set of DV timings after switching the video input or
|
||||
output.</para>
|
||||
<para>To query the capabilities of the DV receiver/transmitter applications can call
|
||||
this ioctl and the driver will fill in the structure. Note that drivers may return
|
||||
different values after switching the video input or output.</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="v4l2-bt-timings-cap">
|
||||
<title>struct <structname>v4l2_bt_timings_cap</structname></title>
|
||||
|
@ -115,7 +109,7 @@ output.</para>
|
|||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>reserved</structfield>[16]</entry>
|
||||
<entry></entry>
|
||||
<entry>Reserved for future extensions. Drivers must set the array to zero.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
<refentry id="vidioc-enum-freq-bands">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl VIDIOC_ENUM_FREQ_BANDS</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>VIDIOC_ENUM_FREQ_BANDS</refname>
|
||||
<refpurpose>Enumerate supported frequency bands</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>struct v4l2_frequency_band
|
||||
*<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>&fd;</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>VIDIOC_ENUM_FREQ_BANDS</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<note>
|
||||
<title>Experimental</title>
|
||||
<para>This is an <link linkend="experimental"> experimental </link>
|
||||
interface and may change in the future.</para>
|
||||
</note>
|
||||
|
||||
<para>Enumerates the frequency bands that a tuner or modulator supports.
|
||||
To do this applications initialize the <structfield>tuner</structfield>,
|
||||
<structfield>type</structfield> and <structfield>index</structfield> fields,
|
||||
and zero out the <structfield>reserved</structfield> array of a &v4l2-frequency-band; and
|
||||
call the <constant>VIDIOC_ENUM_FREQ_BANDS</constant> ioctl with a pointer
|
||||
to this structure.</para>
|
||||
|
||||
<para>This ioctl is supported if the <constant>V4L2_TUNER_CAP_FREQ_BANDS</constant> capability
|
||||
of the corresponding tuner/modulator is set.</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="v4l2-frequency-band">
|
||||
<title>struct <structname>v4l2_frequency_band</structname></title>
|
||||
<tgroup cols="3">
|
||||
&cs-str;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>tuner</structfield></entry>
|
||||
<entry>The tuner or modulator index number. This is the
|
||||
same value as in the &v4l2-input; <structfield>tuner</structfield>
|
||||
field and the &v4l2-tuner; <structfield>index</structfield> field, or
|
||||
the &v4l2-output; <structfield>modulator</structfield> field and the
|
||||
&v4l2-modulator; <structfield>index</structfield> field.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>type</structfield></entry>
|
||||
<entry>The tuner type. This is the same value as in the
|
||||
&v4l2-tuner; <structfield>type</structfield> field. The type must be set
|
||||
to <constant>V4L2_TUNER_RADIO</constant> for <filename>/dev/radioX</filename>
|
||||
device nodes, and to <constant>V4L2_TUNER_ANALOG_TV</constant>
|
||||
for all others. Set this field to <constant>V4L2_TUNER_RADIO</constant> for
|
||||
modulators (currently only radio modulators are supported).
|
||||
See <xref linkend="v4l2-tuner-type" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>index</structfield></entry>
|
||||
<entry>Identifies the frequency band, set by the application.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>capability</structfield></entry>
|
||||
<entry spanname="hspan">The tuner/modulator capability flags for
|
||||
this frequency band, see <xref linkend="tuner-capability" />. The <constant>V4L2_TUNER_CAP_LOW</constant>
|
||||
capability must be the same for all frequency bands of the selected tuner/modulator.
|
||||
So either all bands have that capability set, or none of them have that capability.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>rangelow</structfield></entry>
|
||||
<entry spanname="hspan">The lowest tunable frequency in
|
||||
units of 62.5 kHz, or if the <structfield>capability</structfield>
|
||||
flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
|
||||
Hz, for this frequency band.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>rangehigh</structfield></entry>
|
||||
<entry spanname="hspan">The highest tunable frequency in
|
||||
units of 62.5 kHz, or if the <structfield>capability</structfield>
|
||||
flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
|
||||
Hz, for this frequency band.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>modulation</structfield></entry>
|
||||
<entry spanname="hspan">The supported modulation systems of this frequency band.
|
||||
See <xref linkend="band-modulation" />. Note that currently only one
|
||||
modulation system per frequency band is supported. More work will need to
|
||||
be done if multiple modulation systems are possible. Contact the
|
||||
linux-media mailing list (&v4l-ml;) if you need that functionality.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>reserved</structfield>[9]</entry>
|
||||
<entry>Reserved for future extensions. Applications and drivers
|
||||
must set the array to zero.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table pgwide="1" frame="none" id="band-modulation">
|
||||
<title>Band Modulation Systems</title>
|
||||
<tgroup cols="3">
|
||||
&cs-def;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry><constant>V4L2_BAND_MODULATION_VSB</constant></entry>
|
||||
<entry>0x02</entry>
|
||||
<entry>Vestigial Sideband modulation, used for analog TV.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_BAND_MODULATION_FM</constant></entry>
|
||||
<entry>0x04</entry>
|
||||
<entry>Frequency Modulation, commonly used for analog radio.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_BAND_MODULATION_AM</constant></entry>
|
||||
<entry>0x08</entry>
|
||||
<entry>Amplitude Modulation, commonly used for analog radio.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><errorcode>EINVAL</errorcode></term>
|
||||
<listitem>
|
||||
<para>The <structfield>tuner</structfield> or <structfield>index</structfield>
|
||||
is out of bounds or the <structfield>type</structfield> field is wrong.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
</refentry>
|
|
@ -98,11 +98,12 @@ the &v4l2-output; <structfield>modulator</structfield> field and the
|
|||
<entry>__u32</entry>
|
||||
<entry><structfield>type</structfield></entry>
|
||||
<entry>The tuner type. This is the same value as in the
|
||||
&v4l2-tuner; <structfield>type</structfield> field. See The type must be set
|
||||
&v4l2-tuner; <structfield>type</structfield> field. The type must be set
|
||||
to <constant>V4L2_TUNER_RADIO</constant> for <filename>/dev/radioX</filename>
|
||||
device nodes, and to <constant>V4L2_TUNER_ANALOG_TV</constant>
|
||||
for all others. The field is not applicable to modulators, &ie; ignored
|
||||
by drivers. See <xref linkend="v4l2-tuner-type" /></entry>
|
||||
for all others. Set this field to <constant>V4L2_TUNER_RADIO</constant> for
|
||||
modulators (currently only radio modulators are supported).
|
||||
See <xref linkend="v4l2-tuner-type" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
|
|
|
@ -119,10 +119,14 @@ field is not quite clear.--></para></entry>
|
|||
<xref linkend="tuner-capability" />. Audio flags indicate the ability
|
||||
to decode audio subprograms. They will <emphasis>not</emphasis>
|
||||
change, for example with the current video standard.</para><para>When
|
||||
the structure refers to a radio tuner only the
|
||||
<constant>V4L2_TUNER_CAP_LOW</constant>,
|
||||
<constant>V4L2_TUNER_CAP_STEREO</constant> and
|
||||
<constant>V4L2_TUNER_CAP_RDS</constant> flags can be set.</para></entry>
|
||||
the structure refers to a radio tuner the
|
||||
<constant>V4L2_TUNER_CAP_LANG1</constant>,
|
||||
<constant>V4L2_TUNER_CAP_LANG2</constant> and
|
||||
<constant>V4L2_TUNER_CAP_NORM</constant> flags can't be used.</para>
|
||||
<para>If multiple frequency bands are supported, then
|
||||
<structfield>capability</structfield> is the union of all
|
||||
<structfield>capability></structfield> fields of each &v4l2-frequency-band;.
|
||||
</para></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
|
@ -130,7 +134,9 @@ the structure refers to a radio tuner only the
|
|||
<entry spanname="hspan">The lowest tunable frequency in
|
||||
units of 62.5 kHz, or if the <structfield>capability</structfield>
|
||||
flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
|
||||
Hz.</entry>
|
||||
Hz. If multiple frequency bands are supported, then
|
||||
<structfield>rangelow</structfield> is the lowest frequency
|
||||
of all the frequency bands.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
|
@ -138,7 +144,9 @@ Hz.</entry>
|
|||
<entry spanname="hspan">The highest tunable frequency in
|
||||
units of 62.5 kHz, or if the <structfield>capability</structfield>
|
||||
flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
|
||||
Hz.</entry>
|
||||
Hz. If multiple frequency bands are supported, then
|
||||
<structfield>rangehigh</structfield> is the highest frequency
|
||||
of all the frequency bands.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
|
@ -340,6 +348,12 @@ radio tuners.</entry>
|
|||
<entry>0x0200</entry>
|
||||
<entry>The RDS data is parsed by the hardware and set via controls.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_TUNER_CAP_FREQ_BANDS</constant></entry>
|
||||
<entry>0x0400</entry>
|
||||
<entry>The &VIDIOC-ENUM-FREQ-BANDS; ioctl can be used to enumerate
|
||||
the available frequency bands.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
|
|
@ -191,6 +191,19 @@ linkend="output">Video Output</link> interface.</entry>
|
|||
<link linkend="planar-apis">multi-planar API</link> through the
|
||||
<link linkend="output">Video Output</link> interface.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_CAP_VIDEO_M2M</constant></entry>
|
||||
<entry>0x00004000</entry>
|
||||
<entry>The device supports the single-planar API through the
|
||||
Video Memory-To-Memory interface.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_CAP_VIDEO_M2M_MPLANE</constant></entry>
|
||||
<entry>0x00008000</entry>
|
||||
<entry>The device supports the
|
||||
<link linkend="planar-apis">multi-planar API</link> through the
|
||||
Video Memory-To-Memory interface.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_CAP_VIDEO_OVERLAY</constant></entry>
|
||||
<entry>0x00000004</entry>
|
||||
|
|
|
@ -52,11 +52,23 @@
|
|||
<para>Start a hardware frequency seek from the current frequency.
|
||||
To do this applications initialize the <structfield>tuner</structfield>,
|
||||
<structfield>type</structfield>, <structfield>seek_upward</structfield>,
|
||||
<structfield>spacing</structfield> and
|
||||
<structfield>wrap_around</structfield> fields, and zero out the
|
||||
<structfield>reserved</structfield> array of a &v4l2-hw-freq-seek; and
|
||||
call the <constant>VIDIOC_S_HW_FREQ_SEEK</constant> ioctl with a pointer
|
||||
to this structure.</para>
|
||||
<structfield>wrap_around</structfield>, <structfield>spacing</structfield>,
|
||||
<structfield>rangelow</structfield> and <structfield>rangehigh</structfield>
|
||||
fields, and zero out the <structfield>reserved</structfield> array of a
|
||||
&v4l2-hw-freq-seek; and call the <constant>VIDIOC_S_HW_FREQ_SEEK</constant>
|
||||
ioctl with a pointer to this structure.</para>
|
||||
|
||||
<para>The <structfield>rangelow</structfield> and
|
||||
<structfield>rangehigh</structfield> fields can be set to a non-zero value to
|
||||
tell the driver to search a specific band. If the &v4l2-tuner;
|
||||
<structfield>capability</structfield> field has the
|
||||
<constant>V4L2_TUNER_CAP_HWSEEK_PROG_LIM</constant> flag set, these values
|
||||
must fall within one of the bands returned by &VIDIOC-ENUM-FREQ-BANDS;. If
|
||||
the <constant>V4L2_TUNER_CAP_HWSEEK_PROG_LIM</constant> flag is not set,
|
||||
then these values must exactly match those of one of the bands returned by
|
||||
&VIDIOC-ENUM-FREQ-BANDS;. If the current frequency of the tuner does not fall
|
||||
within the selected band it will be clamped to fit in the band before the
|
||||
seek is started.</para>
|
||||
|
||||
<para>If an error is returned, then the original frequency will
|
||||
be restored.</para>
|
||||
|
@ -102,7 +114,27 @@ field and the &v4l2-tuner; <structfield>index</structfield> field.</entry>
|
|||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>reserved</structfield>[7]</entry>
|
||||
<entry><structfield>rangelow</structfield></entry>
|
||||
<entry>If non-zero, the lowest tunable frequency of the band to
|
||||
search in units of 62.5 kHz, or if the &v4l2-tuner;
|
||||
<structfield>capability</structfield> field has the
|
||||
<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz.
|
||||
If <structfield>rangelow</structfield> is zero a reasonable default value
|
||||
is used.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>rangehigh</structfield></entry>
|
||||
<entry>If non-zero, the highest tunable frequency of the band to
|
||||
search in units of 62.5 kHz, or if the &v4l2-tuner;
|
||||
<structfield>capability</structfield> field has the
|
||||
<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz.
|
||||
If <structfield>rangehigh</structfield> is zero a reasonable default value
|
||||
is used.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>reserved</structfield>[5]</entry>
|
||||
<entry>Reserved for future extensions. Applications
|
||||
must set the array to zero.</entry>
|
||||
</row>
|
||||
|
@ -119,8 +151,10 @@ field and the &v4l2-tuner; <structfield>index</structfield> field.</entry>
|
|||
<term><errorcode>EINVAL</errorcode></term>
|
||||
<listitem>
|
||||
<para>The <structfield>tuner</structfield> index is out of
|
||||
bounds, the wrap_around value is not supported or the value in the <structfield>type</structfield> field is
|
||||
wrong.</para>
|
||||
bounds, the <structfield>wrap_around</structfield> value is not supported or
|
||||
one of the values in the <structfield>type</structfield>,
|
||||
<structfield>rangelow</structfield> or <structfield>rangehigh</structfield>
|
||||
fields is wrong.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
|
|
@ -618,3 +618,17 @@ Why: The regular V4L2 selections and the subdev selection API originally
|
|||
Who: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: Using V4L2_CAP_VIDEO_CAPTURE and V4L2_CAP_VIDEO_OUTPUT flags
|
||||
to indicate a V4L2 memory-to-memory device capability
|
||||
When: 3.8
|
||||
Why: New drivers should use new V4L2_CAP_VIDEO_M2M capability flag
|
||||
to indicate a V4L2 video memory-to-memory (M2M) device and
|
||||
applications can now identify a M2M video device by checking
|
||||
for V4L2_CAP_VIDEO_M2M, with VIDIOC_QUERYCAP ioctl. Using ORed
|
||||
V4L2_CAP_VIDEO_CAPTURE and V4L2_CAP_VIDEO_OUTPUT flags for M2M
|
||||
devices is ambiguous and may lead, for example, to identifying
|
||||
a M2M device as a video capture or output device.
|
||||
Who: Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||
|
||||
----------------------------
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
2 -> Hauppauge HVR850 (au0828) [2040:7240]
|
||||
3 -> DViCO FusionHDTV USB (au0828) [0fe9:d620]
|
||||
4 -> Hauppauge HVR950Q rev xxF8 (au0828) [2040:7201,2040:7211,2040:7281]
|
||||
5 -> Hauppauge Woodbury (au0828) [2040:8200]
|
||||
5 -> Hauppauge Woodbury (au0828) [05e1:0480,2040:8200]
|
||||
|
|
|
@ -159,3 +159,4 @@
|
|||
158 -> Geovision GV-800(S) (slave) [800b:763d,800c:763d,800d:763d]
|
||||
159 -> ProVideo PV183 [1830:1540,1831:1540,1832:1540,1833:1540,1834:1540,1835:1540,1836:1540,1837:1540]
|
||||
160 -> Tongwei Video Technology TD-3116 [f200:3116]
|
||||
161 -> Aposonic W-DVR [0279:0228]
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
17 -> NetUP Dual DVB-S2 CI [1b55:2a2c]
|
||||
18 -> Hauppauge WinTV-HVR1270 [0070:2211]
|
||||
19 -> Hauppauge WinTV-HVR1275 [0070:2215,0070:221d,0070:22f2]
|
||||
20 -> Hauppauge WinTV-HVR1255 [0070:2251,0070:2259,0070:22f1]
|
||||
20 -> Hauppauge WinTV-HVR1255 [0070:2251,0070:22f1]
|
||||
21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295,0070:2299,0070:229d,0070:22f0,0070:22f3,0070:22f4,0070:22f5]
|
||||
22 -> Mygica X8506 DMB-TH [14f1:8651]
|
||||
23 -> Magic-Pro ProHDTV Extreme 2 [14f1:8657]
|
||||
|
@ -33,3 +33,5 @@
|
|||
32 -> MPX-885
|
||||
33 -> Mygica X8507 [14f1:8502]
|
||||
34 -> TerraTec Cinergy T PCIe Dual [153b:117e]
|
||||
35 -> TeVii S471 [d471:9022]
|
||||
36 -> Hauppauge WinTV-HVR1255 [0070:2259]
|
||||
|
|
|
@ -188,3 +188,4 @@
|
|||
187 -> Beholder BeholdTV 503 FM [5ace:5030]
|
||||
188 -> Sensoray 811/911 [6000:0811,6000:0911]
|
||||
189 -> Kworld PC150-U [17de:a134]
|
||||
190 -> Asus My Cinema PS3-100 [1043:48cd]
|
||||
|
|
|
@ -1928,6 +1928,7 @@ static const struct hid_device_id hid_ignore_list[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_RADIOSHARK) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) },
|
||||
|
|
|
@ -333,6 +333,7 @@
|
|||
#define USB_VENDOR_ID_GRIFFIN 0x077d
|
||||
#define USB_DEVICE_ID_POWERMATE 0x0410
|
||||
#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
|
||||
#define USB_DEVICE_ID_RADIOSHARK 0x627a
|
||||
|
||||
#define USB_VENDOR_ID_GTCO 0x078c
|
||||
#define USB_DEVICE_ID_GTCO_90 0x0090
|
||||
|
|
|
@ -756,7 +756,7 @@ retry:
|
|||
* No need to reload base firmware if it matches and if the tuner
|
||||
* is not at sleep mode
|
||||
*/
|
||||
if ((priv->state = XC2028_ACTIVE) &&
|
||||
if ((priv->state == XC2028_ACTIVE) &&
|
||||
(((BASE | new_fw.type) & BASE_TYPES) ==
|
||||
(priv->cur_fw.type & BASE_TYPES))) {
|
||||
tuner_dbg("BASE firmware not changed.\n");
|
||||
|
@ -978,7 +978,7 @@ static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)
|
|||
/* Get AFC */
|
||||
rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
goto ret;
|
||||
|
||||
*afc = afc_reg * 15625; /* Hz */
|
||||
|
||||
|
|
|
@ -210,13 +210,15 @@ struct xc5000_fw_cfg {
|
|||
u16 size;
|
||||
};
|
||||
|
||||
#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
|
||||
static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
|
||||
.name = "dvb-fe-xc5000-1.6.114.fw",
|
||||
.name = XC5000A_FIRMWARE,
|
||||
.size = 12401,
|
||||
};
|
||||
|
||||
#define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw"
|
||||
static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
|
||||
.name = "dvb-fe-xc5000c-41.024.5.fw",
|
||||
.name = XC5000C_FIRMWARE,
|
||||
.size = 16497,
|
||||
};
|
||||
|
||||
|
@ -1259,3 +1261,5 @@ EXPORT_SYMBOL(xc5000_attach);
|
|||
MODULE_AUTHOR("Steven Toth");
|
||||
MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE(XC5000A_FIRMWARE);
|
||||
MODULE_FIRMWARE(XC5000C_FIRMWARE);
|
||||
|
|
|
@ -590,7 +590,7 @@ static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
|
|||
int ret;
|
||||
|
||||
ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
|
||||
memcpy(mac, st->data, sizeof(mac));
|
||||
memcpy(mac, st->data, 6);
|
||||
|
||||
if (ret > 0)
|
||||
deb_info("%s: mac is %pM\n", __func__, mac);
|
||||
|
|
|
@ -2680,12 +2680,14 @@ static int dib8000_tune(struct dvb_frontend *fe)
|
|||
{
|
||||
struct dib8000_state *state = fe->demodulator_priv;
|
||||
int ret = 0;
|
||||
u16 lock, value, mode = fft_to_mode(state);
|
||||
u16 lock, value, mode;
|
||||
|
||||
// we are already tuned - just resuming from suspend
|
||||
if (state == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mode = fft_to_mode(state);
|
||||
|
||||
dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000);
|
||||
dib8000_set_channel(state, 0, 0);
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
static int debug;
|
||||
static int fake_signal_str = 1;
|
||||
|
||||
#define LGS8GXX_FIRMWARE "lgs8g75.fw"
|
||||
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
|
||||
|
||||
|
@ -592,7 +594,7 @@ static int lgs8g75_init_data(struct lgs8gxx_state *priv)
|
|||
int rc;
|
||||
int i;
|
||||
|
||||
rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev);
|
||||
rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -1070,3 +1072,4 @@ EXPORT_SYMBOL(lgs8gxx_attach);
|
|||
MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
|
||||
MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE(LGS8GXX_FIRMWARE);
|
||||
|
|
|
@ -589,7 +589,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (j = 0; j < sizeof(bw_params[j]); j++) {
|
||||
for (j = 0; j < sizeof(bw_params[0]); j++) {
|
||||
ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
|
|
@ -276,16 +276,13 @@ static void smscore_notify_clients(struct smscore_device_t *coredev)
|
|||
static int smscore_notify_callbacks(struct smscore_device_t *coredev,
|
||||
struct device *device, int arrival)
|
||||
{
|
||||
struct list_head *next, *first;
|
||||
struct smscore_device_notifyee_t *elem;
|
||||
int rc = 0;
|
||||
|
||||
/* note: must be called under g_deviceslock */
|
||||
|
||||
first = &g_smscore_notifyees;
|
||||
|
||||
for (next = first->next; next != first; next = next->next) {
|
||||
rc = ((struct smscore_device_notifyee_t *) next)->
|
||||
hotplug(coredev, device, arrival);
|
||||
list_for_each_entry(elem, &g_smscore_notifyees, entry) {
|
||||
rc = elem->hotplug(coredev, device, arrival);
|
||||
if (rc < 0)
|
||||
break;
|
||||
}
|
||||
|
@ -940,29 +937,25 @@ static struct
|
|||
smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,
|
||||
int data_type, int id)
|
||||
{
|
||||
struct smscore_client_t *client = NULL;
|
||||
struct list_head *next, *first;
|
||||
struct list_head *first;
|
||||
struct smscore_client_t *client;
|
||||
unsigned long flags;
|
||||
struct list_head *firstid, *nextid;
|
||||
|
||||
struct list_head *firstid;
|
||||
struct smscore_idlist_t *client_id;
|
||||
|
||||
spin_lock_irqsave(&coredev->clientslock, flags);
|
||||
first = &coredev->clients;
|
||||
for (next = first->next;
|
||||
(next != first) && !client;
|
||||
next = next->next) {
|
||||
firstid = &((struct smscore_client_t *)next)->idlist;
|
||||
for (nextid = firstid->next;
|
||||
nextid != firstid;
|
||||
nextid = nextid->next) {
|
||||
if ((((struct smscore_idlist_t *)nextid)->id == id) &&
|
||||
(((struct smscore_idlist_t *)nextid)->data_type == data_type ||
|
||||
(((struct smscore_idlist_t *)nextid)->data_type == 0))) {
|
||||
client = (struct smscore_client_t *) next;
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(client, first, entry) {
|
||||
firstid = &client->idlist;
|
||||
list_for_each_entry(client_id, firstid, entry) {
|
||||
if ((client_id->id == id) &&
|
||||
(client_id->data_type == data_type ||
|
||||
(client_id->data_type == 0)))
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
client = NULL;
|
||||
found:
|
||||
spin_unlock_irqrestore(&coredev->clientslock, flags);
|
||||
return client;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,39 @@ config RADIO_MAXIRADIO
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called radio-maxiradio.
|
||||
|
||||
config RADIO_SHARK
|
||||
tristate "Griffin radioSHARK USB radio receiver"
|
||||
depends on USB && SND
|
||||
---help---
|
||||
Choose Y here if you have this radio receiver.
|
||||
|
||||
There are 2 versions of this device, this driver is for version 1,
|
||||
which is white.
|
||||
|
||||
In order to control your radio card, you will need to use programs
|
||||
that are compatible with the Video For Linux API. Information on
|
||||
this API and pointers to "v4l" programs may be found at
|
||||
<file:Documentation/video4linux/API.html>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called radio-shark.
|
||||
|
||||
config RADIO_SHARK2
|
||||
tristate "Griffin radioSHARK2 USB radio receiver"
|
||||
depends on USB
|
||||
---help---
|
||||
Choose Y here if you have this radio receiver.
|
||||
|
||||
There are 2 versions of this device, this driver is for version 2,
|
||||
which is black.
|
||||
|
||||
In order to control your radio card, you will need to use programs
|
||||
that are compatible with the Video For Linux API. Information on
|
||||
this API and pointers to "v4l" programs may be found at
|
||||
<file:Documentation/video4linux/API.html>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called radio-shark2.
|
||||
|
||||
config I2C_SI4713
|
||||
tristate "I2C driver for Silicon Labs Si4713 device"
|
||||
|
|
|
@ -11,6 +11,8 @@ obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
|
|||
obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
|
||||
obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
|
||||
obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
|
||||
obj-$(CONFIG_RADIO_SHARK) += radio-shark.o
|
||||
obj-$(CONFIG_RADIO_SHARK2) += shark2.o
|
||||
obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
|
||||
obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
|
||||
obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
|
||||
|
@ -29,4 +31,6 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
|
|||
obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
|
||||
obj-$(CONFIG_RADIO_WL128X) += wl128x/
|
||||
|
||||
shark2-objs := radio-shark2.o radio-tea5777.o
|
||||
|
||||
ccflags-y += -Isound
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
#include <linux/io.h> /* outb, outb_p */
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
#include <media/v4l2-event.h>
|
||||
|
||||
MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
|
||||
MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
|
||||
|
@ -61,14 +64,15 @@ module_param(radio_nr, int, 0);
|
|||
struct cadet {
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct video_device vdev;
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
int io;
|
||||
int users;
|
||||
int curtuner;
|
||||
bool is_fm_band;
|
||||
u32 curfreq;
|
||||
int tunestat;
|
||||
int sigstrength;
|
||||
wait_queue_head_t read_queue;
|
||||
struct timer_list readtimer;
|
||||
__u8 rdsin, rdsout, rdsstat;
|
||||
u8 rdsin, rdsout, rdsstat;
|
||||
unsigned char rdsbuf[RDS_BUFFER];
|
||||
struct mutex lock;
|
||||
int reading;
|
||||
|
@ -81,9 +85,9 @@ static struct cadet cadet_card;
|
|||
* The V4L API spec does not define any particular unit for the signal
|
||||
* strength value. These values are in microvolts of RF at the tuner's input.
|
||||
*/
|
||||
static __u16 sigtable[2][4] = {
|
||||
{ 5, 10, 30, 150 },
|
||||
{ 28, 40, 63, 1000 }
|
||||
static u16 sigtable[2][4] = {
|
||||
{ 1835, 2621, 4128, 65535 },
|
||||
{ 2185, 4369, 13107, 65535 },
|
||||
};
|
||||
|
||||
|
||||
|
@ -91,14 +95,12 @@ static int cadet_getstereo(struct cadet *dev)
|
|||
{
|
||||
int ret = V4L2_TUNER_SUB_MONO;
|
||||
|
||||
if (dev->curtuner != 0) /* Only FM has stereo capability! */
|
||||
if (!dev->is_fm_band) /* Only FM has stereo capability! */
|
||||
return V4L2_TUNER_SUB_MONO;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
outb(7, dev->io); /* Select tuner control */
|
||||
if ((inb(dev->io + 1) & 0x40) == 0)
|
||||
ret = V4L2_TUNER_SUB_STEREO;
|
||||
mutex_unlock(&dev->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -111,8 +113,6 @@ static unsigned cadet_gettune(struct cadet *dev)
|
|||
* Prepare for read
|
||||
*/
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
outb(7, dev->io); /* Select tuner control */
|
||||
curvol = inb(dev->io + 1); /* Save current volume/mute setting */
|
||||
outb(0x00, dev->io + 1); /* Ensure WRITE-ENABLE is LOW */
|
||||
|
@ -134,8 +134,6 @@ static unsigned cadet_gettune(struct cadet *dev)
|
|||
* Restore volume/mute setting
|
||||
*/
|
||||
outb(curvol, dev->io + 1);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return fifo;
|
||||
}
|
||||
|
||||
|
@ -152,20 +150,18 @@ static unsigned cadet_getfreq(struct cadet *dev)
|
|||
/*
|
||||
* Convert to actual frequency
|
||||
*/
|
||||
if (dev->curtuner == 0) { /* FM */
|
||||
test = 12500;
|
||||
for (i = 0; i < 14; i++) {
|
||||
if ((fifo & 0x01) != 0)
|
||||
freq += test;
|
||||
test = test << 1;
|
||||
fifo = fifo >> 1;
|
||||
}
|
||||
freq -= 10700000; /* IF frequency is 10.7 MHz */
|
||||
freq = (freq * 16) / 1000000; /* Make it 1/16 MHz */
|
||||
}
|
||||
if (dev->curtuner == 1) /* AM */
|
||||
freq = ((fifo & 0x7fff) - 2010) * 16;
|
||||
if (!dev->is_fm_band) /* AM */
|
||||
return ((fifo & 0x7fff) - 450) * 16;
|
||||
|
||||
test = 12500;
|
||||
for (i = 0; i < 14; i++) {
|
||||
if ((fifo & 0x01) != 0)
|
||||
freq += test;
|
||||
test = test << 1;
|
||||
fifo = fifo >> 1;
|
||||
}
|
||||
freq -= 10700000; /* IF frequency is 10.7 MHz */
|
||||
freq = (freq * 16) / 1000; /* Make it 1/16 kHz */
|
||||
return freq;
|
||||
}
|
||||
|
||||
|
@ -174,8 +170,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
|
|||
int i;
|
||||
unsigned test;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
outb(7, dev->io); /* Select tuner control */
|
||||
/*
|
||||
* Write the shift register
|
||||
|
@ -194,7 +188,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
|
|||
test = 0x1c | ((fifo >> 23) & 0x02);
|
||||
outb(test, dev->io + 1);
|
||||
}
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
static void cadet_setfreq(struct cadet *dev, unsigned freq)
|
||||
|
@ -203,13 +196,14 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
|
|||
int i, j, test;
|
||||
int curvol;
|
||||
|
||||
dev->curfreq = freq;
|
||||
/*
|
||||
* Formulate a fifo command
|
||||
*/
|
||||
fifo = 0;
|
||||
if (dev->curtuner == 0) { /* FM */
|
||||
if (dev->is_fm_band) { /* FM */
|
||||
test = 102400;
|
||||
freq = (freq * 1000) / 16; /* Make it kHz */
|
||||
freq = freq / 16; /* Make it kHz */
|
||||
freq += 10700; /* IF is 10700 kHz */
|
||||
for (i = 0; i < 14; i++) {
|
||||
fifo = fifo << 1;
|
||||
|
@ -219,20 +213,17 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
|
|||
}
|
||||
test = test >> 1;
|
||||
}
|
||||
}
|
||||
if (dev->curtuner == 1) { /* AM */
|
||||
fifo = (freq / 16) + 2010; /* Make it kHz */
|
||||
fifo |= 0x100000; /* Select AM Band */
|
||||
} else { /* AM */
|
||||
fifo = (freq / 16) + 450; /* Make it kHz */
|
||||
fifo |= 0x100000; /* Select AM Band */
|
||||
}
|
||||
|
||||
/*
|
||||
* Save current volume/mute setting
|
||||
*/
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
outb(7, dev->io); /* Select tuner control */
|
||||
curvol = inb(dev->io + 1);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
/*
|
||||
* Tune the card
|
||||
|
@ -240,49 +231,24 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
|
|||
for (j = 3; j > -1; j--) {
|
||||
cadet_settune(dev, fifo | (j << 16));
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
outb(7, dev->io); /* Select tuner control */
|
||||
outb(curvol, dev->io + 1);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
msleep(100);
|
||||
|
||||
cadet_gettune(dev);
|
||||
if ((dev->tunestat & 0x40) == 0) { /* Tuned */
|
||||
dev->sigstrength = sigtable[dev->curtuner][j];
|
||||
return;
|
||||
dev->sigstrength = sigtable[dev->is_fm_band][j];
|
||||
goto reset_rds;
|
||||
}
|
||||
}
|
||||
dev->sigstrength = 0;
|
||||
reset_rds:
|
||||
outb(3, dev->io);
|
||||
outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
|
||||
}
|
||||
|
||||
|
||||
static int cadet_getvol(struct cadet *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
outb(7, dev->io); /* Select tuner control */
|
||||
if ((inb(dev->io + 1) & 0x20) != 0)
|
||||
ret = 0xffff;
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void cadet_setvol(struct cadet *dev, int vol)
|
||||
{
|
||||
mutex_lock(&dev->lock);
|
||||
outb(7, dev->io); /* Select tuner control */
|
||||
if (vol > 0)
|
||||
outb(0x20, dev->io + 1);
|
||||
else
|
||||
outb(0x00, dev->io + 1);
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
static void cadet_handler(unsigned long data)
|
||||
{
|
||||
struct cadet *dev = (void *)data;
|
||||
|
@ -295,7 +261,7 @@ static void cadet_handler(unsigned long data)
|
|||
outb(0x80, dev->io); /* Select RDS fifo */
|
||||
while ((inb(dev->io) & 0x80) != 0) {
|
||||
dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
|
||||
if (dev->rdsin == dev->rdsout)
|
||||
if (dev->rdsin + 1 == dev->rdsout)
|
||||
printk(KERN_WARNING "cadet: RDS buffer overflow\n");
|
||||
else
|
||||
dev->rdsin++;
|
||||
|
@ -314,11 +280,21 @@ static void cadet_handler(unsigned long data)
|
|||
*/
|
||||
init_timer(&dev->readtimer);
|
||||
dev->readtimer.function = cadet_handler;
|
||||
dev->readtimer.data = (unsigned long)0;
|
||||
dev->readtimer.data = data;
|
||||
dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
|
||||
add_timer(&dev->readtimer);
|
||||
}
|
||||
|
||||
static void cadet_start_rds(struct cadet *dev)
|
||||
{
|
||||
dev->rdsstat = 1;
|
||||
outb(0x80, dev->io); /* Select RDS fifo */
|
||||
init_timer(&dev->readtimer);
|
||||
dev->readtimer.function = cadet_handler;
|
||||
dev->readtimer.data = (unsigned long)dev;
|
||||
dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
|
||||
add_timer(&dev->readtimer);
|
||||
}
|
||||
|
||||
static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
|
||||
{
|
||||
|
@ -327,28 +303,24 @@ static ssize_t cadet_read(struct file *file, char __user *data, size_t count, lo
|
|||
int i = 0;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
if (dev->rdsstat == 0) {
|
||||
dev->rdsstat = 1;
|
||||
outb(0x80, dev->io); /* Select RDS fifo */
|
||||
init_timer(&dev->readtimer);
|
||||
dev->readtimer.function = cadet_handler;
|
||||
dev->readtimer.data = (unsigned long)dev;
|
||||
dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
|
||||
add_timer(&dev->readtimer);
|
||||
}
|
||||
if (dev->rdsstat == 0)
|
||||
cadet_start_rds(dev);
|
||||
if (dev->rdsin == dev->rdsout) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
i = -EWOULDBLOCK;
|
||||
goto unlock;
|
||||
}
|
||||
mutex_unlock(&dev->lock);
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EWOULDBLOCK;
|
||||
interruptible_sleep_on(&dev->read_queue);
|
||||
mutex_lock(&dev->lock);
|
||||
}
|
||||
while (i < count && dev->rdsin != dev->rdsout)
|
||||
readbuf[i++] = dev->rdsbuf[dev->rdsout++];
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
if (copy_to_user(data, readbuf, i))
|
||||
return -EFAULT;
|
||||
if (i && copy_to_user(data, readbuf, i))
|
||||
i = -EFAULT;
|
||||
unlock:
|
||||
mutex_unlock(&dev->lock);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
@ -359,48 +331,58 @@ static int vidioc_querycap(struct file *file, void *priv,
|
|||
strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
|
||||
strlcpy(v->card, "ADS Cadet", sizeof(v->card));
|
||||
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
|
||||
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
|
||||
v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
|
||||
V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
|
||||
v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_frequency_band bands[] = {
|
||||
{
|
||||
.index = 0,
|
||||
.type = V4L2_TUNER_RADIO,
|
||||
.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
|
||||
.rangelow = 8320, /* 520 kHz */
|
||||
.rangehigh = 26400, /* 1650 kHz */
|
||||
.modulation = V4L2_BAND_MODULATION_AM,
|
||||
}, {
|
||||
.index = 1,
|
||||
.type = V4L2_TUNER_RADIO,
|
||||
.capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
|
||||
V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW |
|
||||
V4L2_TUNER_CAP_FREQ_BANDS,
|
||||
.rangelow = 1400000, /* 87.5 MHz */
|
||||
.rangehigh = 1728000, /* 108.0 MHz */
|
||||
.modulation = V4L2_BAND_MODULATION_FM,
|
||||
},
|
||||
};
|
||||
|
||||
static int vidioc_g_tuner(struct file *file, void *priv,
|
||||
struct v4l2_tuner *v)
|
||||
{
|
||||
struct cadet *dev = video_drvdata(file);
|
||||
|
||||
if (v->index)
|
||||
return -EINVAL;
|
||||
v->type = V4L2_TUNER_RADIO;
|
||||
switch (v->index) {
|
||||
case 0:
|
||||
strlcpy(v->name, "FM", sizeof(v->name));
|
||||
v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
|
||||
V4L2_TUNER_CAP_RDS_BLOCK_IO;
|
||||
v->rangelow = 1400; /* 87.5 MHz */
|
||||
v->rangehigh = 1728; /* 108.0 MHz */
|
||||
strlcpy(v->name, "Radio", sizeof(v->name));
|
||||
v->capability = bands[0].capability | bands[1].capability;
|
||||
v->rangelow = bands[0].rangelow; /* 520 kHz (start of AM band) */
|
||||
v->rangehigh = bands[1].rangehigh; /* 108.0 MHz (end of FM band) */
|
||||
if (dev->is_fm_band) {
|
||||
v->rxsubchans = cadet_getstereo(dev);
|
||||
switch (v->rxsubchans) {
|
||||
case V4L2_TUNER_SUB_MONO:
|
||||
v->audmode = V4L2_TUNER_MODE_MONO;
|
||||
break;
|
||||
case V4L2_TUNER_SUB_STEREO:
|
||||
v->audmode = V4L2_TUNER_MODE_STEREO;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
v->rxsubchans |= V4L2_TUNER_SUB_RDS;
|
||||
break;
|
||||
case 1:
|
||||
strlcpy(v->name, "AM", sizeof(v->name));
|
||||
v->capability = V4L2_TUNER_CAP_LOW;
|
||||
outb(3, dev->io);
|
||||
outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
|
||||
mdelay(100);
|
||||
outb(3, dev->io);
|
||||
if (inb(dev->io + 1) & 0x80)
|
||||
v->rxsubchans |= V4L2_TUNER_SUB_RDS;
|
||||
} else {
|
||||
v->rangelow = 8320; /* 520 kHz */
|
||||
v->rangehigh = 26400; /* 1650 kHz */
|
||||
v->rxsubchans = V4L2_TUNER_SUB_MONO;
|
||||
v->audmode = V4L2_TUNER_MODE_MONO;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
v->audmode = V4L2_TUNER_MODE_STEREO;
|
||||
v->signal = dev->sigstrength; /* We might need to modify scaling of this */
|
||||
return 0;
|
||||
}
|
||||
|
@ -408,11 +390,17 @@ static int vidioc_g_tuner(struct file *file, void *priv,
|
|||
static int vidioc_s_tuner(struct file *file, void *priv,
|
||||
struct v4l2_tuner *v)
|
||||
{
|
||||
struct cadet *dev = video_drvdata(file);
|
||||
return v->index ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
if (v->index != 0 && v->index != 1)
|
||||
static int vidioc_enum_freq_bands(struct file *file, void *priv,
|
||||
struct v4l2_frequency_band *band)
|
||||
{
|
||||
if (band->tuner)
|
||||
return -EINVAL;
|
||||
dev->curtuner = v->index;
|
||||
if (band->index >= ARRAY_SIZE(bands))
|
||||
return -EINVAL;
|
||||
*band = bands[band->index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -421,9 +409,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
|
|||
{
|
||||
struct cadet *dev = video_drvdata(file);
|
||||
|
||||
f->tuner = dev->curtuner;
|
||||
if (f->tuner)
|
||||
return -EINVAL;
|
||||
f->type = V4L2_TUNER_RADIO;
|
||||
f->frequency = cadet_getfreq(dev);
|
||||
f->frequency = dev->curfreq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -433,103 +422,46 @@ static int vidioc_s_frequency(struct file *file, void *priv,
|
|||
{
|
||||
struct cadet *dev = video_drvdata(file);
|
||||
|
||||
if (f->type != V4L2_TUNER_RADIO)
|
||||
return -EINVAL;
|
||||
if (dev->curtuner == 0 && (f->frequency < 1400 || f->frequency > 1728))
|
||||
return -EINVAL;
|
||||
if (dev->curtuner == 1 && (f->frequency < 8320 || f->frequency > 26400))
|
||||
if (f->tuner)
|
||||
return -EINVAL;
|
||||
dev->is_fm_band =
|
||||
f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2;
|
||||
clamp(f->frequency, bands[dev->is_fm_band].rangelow,
|
||||
bands[dev->is_fm_band].rangehigh);
|
||||
cadet_setfreq(dev, f->frequency);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_queryctrl(struct file *file, void *priv,
|
||||
struct v4l2_queryctrl *qc)
|
||||
static int cadet_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
switch (qc->id) {
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vidioc_g_ctrl(struct file *file, void *priv,
|
||||
struct v4l2_control *ctrl)
|
||||
{
|
||||
struct cadet *dev = video_drvdata(file);
|
||||
struct cadet *dev = container_of(ctrl->handler, struct cadet, ctrl_handler);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
|
||||
ctrl->value = (cadet_getvol(dev) == 0);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
ctrl->value = cadet_getvol(dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_ctrl(struct file *file, void *priv,
|
||||
struct v4l2_control *ctrl)
|
||||
{
|
||||
struct cadet *dev = video_drvdata(file);
|
||||
|
||||
switch (ctrl->id){
|
||||
case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
|
||||
if (ctrl->value)
|
||||
cadet_setvol(dev, 0);
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
outb(7, dev->io); /* Select tuner control */
|
||||
if (ctrl->val)
|
||||
outb(0x00, dev->io + 1);
|
||||
else
|
||||
cadet_setvol(dev, 0xffff);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
cadet_setvol(dev, ctrl->value);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
outb(0x20, dev->io + 1);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
|
||||
{
|
||||
*i = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
|
||||
{
|
||||
return i ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_audio(struct file *file, void *priv,
|
||||
struct v4l2_audio *a)
|
||||
{
|
||||
a->index = 0;
|
||||
strlcpy(a->name, "Radio", sizeof(a->name));
|
||||
a->capability = V4L2_AUDCAP_STEREO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_audio(struct file *file, void *priv,
|
||||
struct v4l2_audio *a)
|
||||
{
|
||||
return a->index ? -EINVAL : 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int cadet_open(struct file *file)
|
||||
{
|
||||
struct cadet *dev = video_drvdata(file);
|
||||
int err;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
dev->users++;
|
||||
if (1 == dev->users)
|
||||
err = v4l2_fh_open(file);
|
||||
if (err)
|
||||
goto fail;
|
||||
if (v4l2_fh_is_singular_file(file))
|
||||
init_waitqueue_head(&dev->read_queue);
|
||||
fail:
|
||||
mutex_unlock(&dev->lock);
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cadet_release(struct file *file)
|
||||
|
@ -537,11 +469,11 @@ static int cadet_release(struct file *file)
|
|||
struct cadet *dev = video_drvdata(file);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
dev->users--;
|
||||
if (0 == dev->users) {
|
||||
if (v4l2_fh_is_singular_file(file) && dev->rdsstat) {
|
||||
del_timer_sync(&dev->readtimer);
|
||||
dev->rdsstat = 0;
|
||||
}
|
||||
v4l2_fh_release(file);
|
||||
mutex_unlock(&dev->lock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -549,11 +481,19 @@ static int cadet_release(struct file *file)
|
|||
static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait)
|
||||
{
|
||||
struct cadet *dev = video_drvdata(file);
|
||||
unsigned long req_events = poll_requested_events(wait);
|
||||
unsigned int res = v4l2_ctrl_poll(file, wait);
|
||||
|
||||
poll_wait(file, &dev->read_queue, wait);
|
||||
if (dev->rdsstat == 0 && (req_events & (POLLIN | POLLRDNORM))) {
|
||||
mutex_lock(&dev->lock);
|
||||
if (dev->rdsstat == 0)
|
||||
cadet_start_rds(dev);
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
if (dev->rdsin != dev->rdsout)
|
||||
return POLLIN | POLLRDNORM;
|
||||
return 0;
|
||||
res |= POLLIN | POLLRDNORM;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
@ -572,13 +512,14 @@ static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
|
|||
.vidioc_s_tuner = vidioc_s_tuner,
|
||||
.vidioc_g_frequency = vidioc_g_frequency,
|
||||
.vidioc_s_frequency = vidioc_s_frequency,
|
||||
.vidioc_queryctrl = vidioc_queryctrl,
|
||||
.vidioc_g_ctrl = vidioc_g_ctrl,
|
||||
.vidioc_s_ctrl = vidioc_s_ctrl,
|
||||
.vidioc_g_audio = vidioc_g_audio,
|
||||
.vidioc_s_audio = vidioc_s_audio,
|
||||
.vidioc_g_input = vidioc_g_input,
|
||||
.vidioc_s_input = vidioc_s_input,
|
||||
.vidioc_enum_freq_bands = vidioc_enum_freq_bands,
|
||||
.vidioc_log_status = v4l2_ctrl_log_status,
|
||||
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
||||
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_ops cadet_ctrl_ops = {
|
||||
.s_ctrl = cadet_s_ctrl,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
|
@ -628,8 +569,8 @@ static void cadet_probe(struct cadet *dev)
|
|||
for (i = 0; i < 8; i++) {
|
||||
dev->io = iovals[i];
|
||||
if (request_region(dev->io, 2, "cadet-probe")) {
|
||||
cadet_setfreq(dev, 1410);
|
||||
if (cadet_getfreq(dev) == 1410) {
|
||||
cadet_setfreq(dev, bands[1].rangelow);
|
||||
if (cadet_getfreq(dev) == bands[1].rangelow) {
|
||||
release_region(dev->io, 2);
|
||||
return;
|
||||
}
|
||||
|
@ -648,7 +589,8 @@ static int __init cadet_init(void)
|
|||
{
|
||||
struct cadet *dev = &cadet_card;
|
||||
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
|
||||
int res;
|
||||
struct v4l2_ctrl_handler *hdl;
|
||||
int res = -ENODEV;
|
||||
|
||||
strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
|
||||
mutex_init(&dev->lock);
|
||||
|
@ -680,23 +622,40 @@ static int __init cadet_init(void)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
hdl = &dev->ctrl_handler;
|
||||
v4l2_ctrl_handler_init(hdl, 2);
|
||||
v4l2_ctrl_new_std(hdl, &cadet_ctrl_ops,
|
||||
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
|
||||
v4l2_dev->ctrl_handler = hdl;
|
||||
if (hdl->error) {
|
||||
res = hdl->error;
|
||||
v4l2_err(v4l2_dev, "Could not register controls\n");
|
||||
goto err_hdl;
|
||||
}
|
||||
|
||||
dev->is_fm_band = true;
|
||||
dev->curfreq = bands[dev->is_fm_band].rangelow;
|
||||
cadet_setfreq(dev, dev->curfreq);
|
||||
strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
|
||||
dev->vdev.v4l2_dev = v4l2_dev;
|
||||
dev->vdev.fops = &cadet_fops;
|
||||
dev->vdev.ioctl_ops = &cadet_ioctl_ops;
|
||||
dev->vdev.release = video_device_release_empty;
|
||||
dev->vdev.lock = &dev->lock;
|
||||
set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
|
||||
video_set_drvdata(&dev->vdev, dev);
|
||||
|
||||
if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
|
||||
v4l2_device_unregister(v4l2_dev);
|
||||
release_region(dev->io, 2);
|
||||
goto fail;
|
||||
}
|
||||
if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
|
||||
goto err_hdl;
|
||||
v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
|
||||
return 0;
|
||||
err_hdl:
|
||||
v4l2_ctrl_handler_free(hdl);
|
||||
v4l2_device_unregister(v4l2_dev);
|
||||
release_region(dev->io, 2);
|
||||
fail:
|
||||
pnp_unregister_driver(&cadet_pnp_driver);
|
||||
return -ENODEV;
|
||||
return res;
|
||||
}
|
||||
|
||||
static void __exit cadet_exit(void)
|
||||
|
@ -704,7 +663,10 @@ static void __exit cadet_exit(void)
|
|||
struct cadet *dev = &cadet_card;
|
||||
|
||||
video_unregister_device(&dev->vdev);
|
||||
v4l2_ctrl_handler_free(&dev->ctrl_handler);
|
||||
v4l2_device_unregister(&dev->v4l2_dev);
|
||||
outb(7, dev->io); /* Mute */
|
||||
outb(0x00, dev->io + 1);
|
||||
release_region(dev->io, 2);
|
||||
pnp_unregister_driver(&cadet_pnp_driver);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* Linux V4L2 radio driver for the Griffin radioSHARK USB radio receiver
|
||||
*
|
||||
* Note the radioSHARK offers the audio through a regular USB audio device,
|
||||
* this driver only handles the tuning.
|
||||
*
|
||||
* The info necessary to drive the shark was taken from the small userspace
|
||||
* shark.c program by Michael Rolig, which he kindly placed in the Public
|
||||
* Domain.
|
||||
*
|
||||
* Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <sound/tea575x-tuner.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Griffin radioSHARK, USB radio receiver driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define SHARK_IN_EP 0x83
|
||||
#define SHARK_OUT_EP 0x05
|
||||
|
||||
#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */
|
||||
#define TEA575X_BIT_BAND_MASK (3<<20)
|
||||
#define TEA575X_BIT_BAND_FM (0<<20)
|
||||
|
||||
#define TB_LEN 6
|
||||
#define DRV_NAME "radioshark"
|
||||
|
||||
#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
|
||||
|
||||
enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS };
|
||||
|
||||
static void shark_led_set_blue(struct led_classdev *led_cdev,
|
||||
enum led_brightness value);
|
||||
static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
|
||||
enum led_brightness value);
|
||||
static void shark_led_set_red(struct led_classdev *led_cdev,
|
||||
enum led_brightness value);
|
||||
|
||||
static const struct led_classdev shark_led_templates[NO_LEDS] = {
|
||||
[BLUE_LED] = {
|
||||
.name = "%s:blue:",
|
||||
.brightness = LED_OFF,
|
||||
.max_brightness = 127,
|
||||
.brightness_set = shark_led_set_blue,
|
||||
},
|
||||
[BLUE_PULSE_LED] = {
|
||||
.name = "%s:blue-pulse:",
|
||||
.brightness = LED_OFF,
|
||||
.max_brightness = 255,
|
||||
.brightness_set = shark_led_set_blue_pulse,
|
||||
},
|
||||
[RED_LED] = {
|
||||
.name = "%s:red:",
|
||||
.brightness = LED_OFF,
|
||||
.max_brightness = 1,
|
||||
.brightness_set = shark_led_set_red,
|
||||
},
|
||||
};
|
||||
|
||||
struct shark_device {
|
||||
struct usb_device *usbdev;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct snd_tea575x tea;
|
||||
|
||||
struct work_struct led_work;
|
||||
struct led_classdev leds[NO_LEDS];
|
||||
char led_names[NO_LEDS][32];
|
||||
atomic_t brightness[NO_LEDS];
|
||||
unsigned long brightness_new;
|
||||
|
||||
u8 *transfer_buffer;
|
||||
u32 last_val;
|
||||
};
|
||||
|
||||
static atomic_t shark_instance = ATOMIC_INIT(0);
|
||||
|
||||
static void shark_write_val(struct snd_tea575x *tea, u32 val)
|
||||
{
|
||||
struct shark_device *shark = tea->private_data;
|
||||
int i, res, actual_len;
|
||||
|
||||
/* Avoid unnecessary (slow) USB transfers */
|
||||
if (shark->last_val == val)
|
||||
return;
|
||||
|
||||
memset(shark->transfer_buffer, 0, TB_LEN);
|
||||
shark->transfer_buffer[0] = 0xc0; /* Write shift register command */
|
||||
for (i = 0; i < 4; i++)
|
||||
shark->transfer_buffer[i] |= (val >> (24 - i * 8)) & 0xff;
|
||||
|
||||
res = usb_interrupt_msg(shark->usbdev,
|
||||
usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
|
||||
shark->transfer_buffer, TB_LEN,
|
||||
&actual_len, 1000);
|
||||
if (res >= 0)
|
||||
shark->last_val = val;
|
||||
else
|
||||
v4l2_err(&shark->v4l2_dev, "set-freq error: %d\n", res);
|
||||
}
|
||||
|
||||
static u32 shark_read_val(struct snd_tea575x *tea)
|
||||
{
|
||||
struct shark_device *shark = tea->private_data;
|
||||
int i, res, actual_len;
|
||||
u32 val = 0;
|
||||
|
||||
memset(shark->transfer_buffer, 0, TB_LEN);
|
||||
shark->transfer_buffer[0] = 0x80;
|
||||
res = usb_interrupt_msg(shark->usbdev,
|
||||
usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
|
||||
shark->transfer_buffer, TB_LEN,
|
||||
&actual_len, 1000);
|
||||
if (res < 0) {
|
||||
v4l2_err(&shark->v4l2_dev, "request-status error: %d\n", res);
|
||||
return shark->last_val;
|
||||
}
|
||||
|
||||
res = usb_interrupt_msg(shark->usbdev,
|
||||
usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
|
||||
shark->transfer_buffer, TB_LEN,
|
||||
&actual_len, 1000);
|
||||
if (res < 0) {
|
||||
v4l2_err(&shark->v4l2_dev, "get-status error: %d\n", res);
|
||||
return shark->last_val;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
val |= shark->transfer_buffer[i] << (24 - i * 8);
|
||||
|
||||
shark->last_val = val;
|
||||
|
||||
/*
|
||||
* The shark does not allow actually reading the stereo / mono pin :(
|
||||
* So assume that when we're tuned to an FM station and mono has not
|
||||
* been requested, that we're receiving stereo.
|
||||
*/
|
||||
if (((val & TEA575X_BIT_BAND_MASK) == TEA575X_BIT_BAND_FM) &&
|
||||
!(val & TEA575X_BIT_MONO))
|
||||
shark->tea.stereo = true;
|
||||
else
|
||||
shark->tea.stereo = false;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static struct snd_tea575x_ops shark_tea_ops = {
|
||||
.write_val = shark_write_val,
|
||||
.read_val = shark_read_val,
|
||||
};
|
||||
|
||||
static void shark_led_work(struct work_struct *work)
|
||||
{
|
||||
struct shark_device *shark =
|
||||
container_of(work, struct shark_device, led_work);
|
||||
int i, res, brightness, actual_len;
|
||||
|
||||
/*
|
||||
* We use the v4l2_dev lock and registered bit to ensure the device
|
||||
* does not get unplugged and unreffed while we're running.
|
||||
*/
|
||||
mutex_lock(&shark->tea.mutex);
|
||||
if (!video_is_registered(&shark->tea.vd))
|
||||
goto leave;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!test_and_clear_bit(i, &shark->brightness_new))
|
||||
continue;
|
||||
|
||||
brightness = atomic_read(&shark->brightness[i]);
|
||||
memset(shark->transfer_buffer, 0, TB_LEN);
|
||||
if (i != RED_LED) {
|
||||
shark->transfer_buffer[0] = 0xA0 + i;
|
||||
shark->transfer_buffer[1] = brightness;
|
||||
} else
|
||||
shark->transfer_buffer[0] = brightness ? 0xA9 : 0xA8;
|
||||
res = usb_interrupt_msg(shark->usbdev,
|
||||
usb_sndintpipe(shark->usbdev, 0x05),
|
||||
shark->transfer_buffer, TB_LEN,
|
||||
&actual_len, 1000);
|
||||
if (res < 0)
|
||||
v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
|
||||
shark->led_names[i], res);
|
||||
}
|
||||
leave:
|
||||
mutex_unlock(&shark->tea.mutex);
|
||||
}
|
||||
|
||||
static void shark_led_set_blue(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct shark_device *shark =
|
||||
container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
|
||||
|
||||
atomic_set(&shark->brightness[BLUE_LED], value);
|
||||
set_bit(BLUE_LED, &shark->brightness_new);
|
||||
schedule_work(&shark->led_work);
|
||||
}
|
||||
|
||||
static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct shark_device *shark = container_of(led_cdev,
|
||||
struct shark_device, leds[BLUE_PULSE_LED]);
|
||||
|
||||
atomic_set(&shark->brightness[BLUE_PULSE_LED], 256 - value);
|
||||
set_bit(BLUE_PULSE_LED, &shark->brightness_new);
|
||||
schedule_work(&shark->led_work);
|
||||
}
|
||||
|
||||
static void shark_led_set_red(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct shark_device *shark =
|
||||
container_of(led_cdev, struct shark_device, leds[RED_LED]);
|
||||
|
||||
atomic_set(&shark->brightness[RED_LED], value);
|
||||
set_bit(RED_LED, &shark->brightness_new);
|
||||
schedule_work(&shark->led_work);
|
||||
}
|
||||
|
||||
static void usb_shark_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
|
||||
struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
|
||||
int i;
|
||||
|
||||
mutex_lock(&shark->tea.mutex);
|
||||
v4l2_device_disconnect(&shark->v4l2_dev);
|
||||
snd_tea575x_exit(&shark->tea);
|
||||
mutex_unlock(&shark->tea.mutex);
|
||||
|
||||
for (i = 0; i < NO_LEDS; i++)
|
||||
led_classdev_unregister(&shark->leds[i]);
|
||||
|
||||
v4l2_device_put(&shark->v4l2_dev);
|
||||
}
|
||||
|
||||
static void usb_shark_release(struct v4l2_device *v4l2_dev)
|
||||
{
|
||||
struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
|
||||
|
||||
cancel_work_sync(&shark->led_work);
|
||||
v4l2_device_unregister(&shark->v4l2_dev);
|
||||
kfree(shark->transfer_buffer);
|
||||
kfree(shark);
|
||||
}
|
||||
|
||||
static int usb_shark_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct shark_device *shark;
|
||||
int i, retval = -ENOMEM;
|
||||
|
||||
shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
|
||||
if (!shark)
|
||||
return retval;
|
||||
|
||||
shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
|
||||
if (!shark->transfer_buffer)
|
||||
goto err_alloc_buffer;
|
||||
|
||||
/*
|
||||
* Work around a bug in usbhid/hid-core.c, where it leaves a dangling
|
||||
* pointer in intfdata causing v4l2-device.c to not set it. Which
|
||||
* results in usb_shark_disconnect() referencing the dangling pointer
|
||||
*
|
||||
* REMOVE (as soon as the above bug is fixed, patch submitted)
|
||||
*/
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
shark->v4l2_dev.release = usb_shark_release;
|
||||
v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
|
||||
retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
|
||||
if (retval) {
|
||||
v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
|
||||
goto err_reg_dev;
|
||||
}
|
||||
|
||||
shark->usbdev = interface_to_usbdev(intf);
|
||||
shark->tea.v4l2_dev = &shark->v4l2_dev;
|
||||
shark->tea.private_data = shark;
|
||||
shark->tea.radio_nr = -1;
|
||||
shark->tea.ops = &shark_tea_ops;
|
||||
shark->tea.cannot_mute = true;
|
||||
strlcpy(shark->tea.card, "Griffin radioSHARK",
|
||||
sizeof(shark->tea.card));
|
||||
usb_make_path(shark->usbdev, shark->tea.bus_info,
|
||||
sizeof(shark->tea.bus_info));
|
||||
|
||||
retval = snd_tea575x_init(&shark->tea, THIS_MODULE);
|
||||
if (retval) {
|
||||
v4l2_err(&shark->v4l2_dev, "couldn't init tea5757\n");
|
||||
goto err_init_tea;
|
||||
}
|
||||
|
||||
INIT_WORK(&shark->led_work, shark_led_work);
|
||||
for (i = 0; i < NO_LEDS; i++) {
|
||||
shark->leds[i] = shark_led_templates[i];
|
||||
snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
|
||||
shark->leds[i].name, shark->v4l2_dev.name);
|
||||
shark->leds[i].name = shark->led_names[i];
|
||||
/*
|
||||
* We don't fail the probe if we fail to register the leds,
|
||||
* because once we've called snd_tea575x_init, the /dev/radio0
|
||||
* node may be opened from userspace holding a reference to us!
|
||||
*
|
||||
* Note we cannot register the leds first instead as
|
||||
* shark_led_work depends on the v4l2 mutex and registered bit.
|
||||
*/
|
||||
retval = led_classdev_register(&intf->dev, &shark->leds[i]);
|
||||
if (retval)
|
||||
v4l2_err(&shark->v4l2_dev,
|
||||
"couldn't register led: %s\n",
|
||||
shark->led_names[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_init_tea:
|
||||
v4l2_device_unregister(&shark->v4l2_dev);
|
||||
err_reg_dev:
|
||||
kfree(shark->transfer_buffer);
|
||||
err_alloc_buffer:
|
||||
kfree(shark);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
|
||||
static struct usb_device_id usb_shark_device_table[] = {
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
|
||||
USB_DEVICE_ID_MATCH_INT_CLASS,
|
||||
.idVendor = 0x077d,
|
||||
.idProduct = 0x627a,
|
||||
.bcdDevice_lo = 0x0001,
|
||||
.bcdDevice_hi = 0x0001,
|
||||
.bInterfaceClass = 3,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
|
||||
|
||||
static struct usb_driver usb_shark_driver = {
|
||||
.name = DRV_NAME,
|
||||
.probe = usb_shark_probe,
|
||||
.disconnect = usb_shark_disconnect,
|
||||
.id_table = usb_shark_device_table,
|
||||
};
|
||||
module_usb_driver(usb_shark_driver);
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Linux V4L2 radio driver for the Griffin radioSHARK2 USB radio receiver
|
||||
*
|
||||
* Note the radioSHARK2 offers the audio through a regular USB audio device,
|
||||
* this driver only handles the tuning.
|
||||
*
|
||||
* The info necessary to drive the shark2 was taken from the small userspace
|
||||
* shark2.c program by Hisaaki Shibata, which he kindly placed in the Public
|
||||
* Domain.
|
||||
*
|
||||
* Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include "radio-tea5777.h"
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0-1)");
|
||||
|
||||
|
||||
#define SHARK_IN_EP 0x83
|
||||
#define SHARK_OUT_EP 0x05
|
||||
|
||||
#define TB_LEN 7
|
||||
#define DRV_NAME "radioshark2"
|
||||
|
||||
#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
|
||||
|
||||
enum { BLUE_LED, RED_LED, NO_LEDS };
|
||||
|
||||
static void shark_led_set_blue(struct led_classdev *led_cdev,
|
||||
enum led_brightness value);
|
||||
static void shark_led_set_red(struct led_classdev *led_cdev,
|
||||
enum led_brightness value);
|
||||
|
||||
static const struct led_classdev shark_led_templates[NO_LEDS] = {
|
||||
[BLUE_LED] = {
|
||||
.name = "%s:blue:",
|
||||
.brightness = LED_OFF,
|
||||
.max_brightness = 127,
|
||||
.brightness_set = shark_led_set_blue,
|
||||
},
|
||||
[RED_LED] = {
|
||||
.name = "%s:red:",
|
||||
.brightness = LED_OFF,
|
||||
.max_brightness = 1,
|
||||
.brightness_set = shark_led_set_red,
|
||||
},
|
||||
};
|
||||
|
||||
struct shark_device {
|
||||
struct usb_device *usbdev;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct radio_tea5777 tea;
|
||||
|
||||
struct work_struct led_work;
|
||||
struct led_classdev leds[NO_LEDS];
|
||||
char led_names[NO_LEDS][32];
|
||||
atomic_t brightness[NO_LEDS];
|
||||
unsigned long brightness_new;
|
||||
|
||||
u8 *transfer_buffer;
|
||||
};
|
||||
|
||||
static atomic_t shark_instance = ATOMIC_INIT(0);
|
||||
|
||||
static int shark_write_reg(struct radio_tea5777 *tea, u64 reg)
|
||||
{
|
||||
struct shark_device *shark = tea->private_data;
|
||||
int i, res, actual_len;
|
||||
|
||||
memset(shark->transfer_buffer, 0, TB_LEN);
|
||||
shark->transfer_buffer[0] = 0x81; /* Write register command */
|
||||
for (i = 0; i < 6; i++)
|
||||
shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff;
|
||||
|
||||
v4l2_dbg(1, debug, tea->v4l2_dev,
|
||||
"shark2-write: %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
shark->transfer_buffer[0], shark->transfer_buffer[1],
|
||||
shark->transfer_buffer[2], shark->transfer_buffer[3],
|
||||
shark->transfer_buffer[4], shark->transfer_buffer[5],
|
||||
shark->transfer_buffer[6]);
|
||||
|
||||
res = usb_interrupt_msg(shark->usbdev,
|
||||
usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
|
||||
shark->transfer_buffer, TB_LEN,
|
||||
&actual_len, 1000);
|
||||
if (res < 0) {
|
||||
v4l2_err(tea->v4l2_dev, "write error: %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret)
|
||||
{
|
||||
struct shark_device *shark = tea->private_data;
|
||||
int i, res, actual_len;
|
||||
u32 reg = 0;
|
||||
|
||||
memset(shark->transfer_buffer, 0, TB_LEN);
|
||||
shark->transfer_buffer[0] = 0x82;
|
||||
res = usb_interrupt_msg(shark->usbdev,
|
||||
usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
|
||||
shark->transfer_buffer, TB_LEN,
|
||||
&actual_len, 1000);
|
||||
if (res < 0) {
|
||||
v4l2_err(tea->v4l2_dev, "request-read error: %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = usb_interrupt_msg(shark->usbdev,
|
||||
usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
|
||||
shark->transfer_buffer, TB_LEN,
|
||||
&actual_len, 1000);
|
||||
if (res < 0) {
|
||||
v4l2_err(tea->v4l2_dev, "read error: %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
reg |= shark->transfer_buffer[i] << (16 - i * 8);
|
||||
|
||||
v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %02x %02x %02x\n",
|
||||
shark->transfer_buffer[0], shark->transfer_buffer[1],
|
||||
shark->transfer_buffer[2]);
|
||||
|
||||
*reg_ret = reg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct radio_tea5777_ops shark_tea_ops = {
|
||||
.write_reg = shark_write_reg,
|
||||
.read_reg = shark_read_reg,
|
||||
};
|
||||
|
||||
static void shark_led_work(struct work_struct *work)
|
||||
{
|
||||
struct shark_device *shark =
|
||||
container_of(work, struct shark_device, led_work);
|
||||
int i, res, brightness, actual_len;
|
||||
/*
|
||||
* We use the v4l2_dev lock and registered bit to ensure the device
|
||||
* does not get unplugged and unreffed while we're running.
|
||||
*/
|
||||
mutex_lock(&shark->tea.mutex);
|
||||
if (!video_is_registered(&shark->tea.vd))
|
||||
goto leave;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!test_and_clear_bit(i, &shark->brightness_new))
|
||||
continue;
|
||||
|
||||
brightness = atomic_read(&shark->brightness[i]);
|
||||
memset(shark->transfer_buffer, 0, TB_LEN);
|
||||
shark->transfer_buffer[0] = 0x83 + i;
|
||||
shark->transfer_buffer[1] = brightness;
|
||||
res = usb_interrupt_msg(shark->usbdev,
|
||||
usb_sndintpipe(shark->usbdev,
|
||||
SHARK_OUT_EP),
|
||||
shark->transfer_buffer, TB_LEN,
|
||||
&actual_len, 1000);
|
||||
if (res < 0)
|
||||
v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
|
||||
shark->led_names[i], res);
|
||||
}
|
||||
leave:
|
||||
mutex_unlock(&shark->tea.mutex);
|
||||
}
|
||||
|
||||
static void shark_led_set_blue(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct shark_device *shark =
|
||||
container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
|
||||
|
||||
atomic_set(&shark->brightness[BLUE_LED], value);
|
||||
set_bit(BLUE_LED, &shark->brightness_new);
|
||||
schedule_work(&shark->led_work);
|
||||
}
|
||||
|
||||
static void shark_led_set_red(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct shark_device *shark =
|
||||
container_of(led_cdev, struct shark_device, leds[RED_LED]);
|
||||
|
||||
atomic_set(&shark->brightness[RED_LED], value);
|
||||
set_bit(RED_LED, &shark->brightness_new);
|
||||
schedule_work(&shark->led_work);
|
||||
}
|
||||
|
||||
static void usb_shark_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
|
||||
struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
|
||||
int i;
|
||||
|
||||
mutex_lock(&shark->tea.mutex);
|
||||
v4l2_device_disconnect(&shark->v4l2_dev);
|
||||
radio_tea5777_exit(&shark->tea);
|
||||
mutex_unlock(&shark->tea.mutex);
|
||||
|
||||
for (i = 0; i < NO_LEDS; i++)
|
||||
led_classdev_unregister(&shark->leds[i]);
|
||||
|
||||
v4l2_device_put(&shark->v4l2_dev);
|
||||
}
|
||||
|
||||
static void usb_shark_release(struct v4l2_device *v4l2_dev)
|
||||
{
|
||||
struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
|
||||
|
||||
cancel_work_sync(&shark->led_work);
|
||||
v4l2_device_unregister(&shark->v4l2_dev);
|
||||
kfree(shark->transfer_buffer);
|
||||
kfree(shark);
|
||||
}
|
||||
|
||||
static int usb_shark_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct shark_device *shark;
|
||||
int i, retval = -ENOMEM;
|
||||
|
||||
shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
|
||||
if (!shark)
|
||||
return retval;
|
||||
|
||||
shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
|
||||
if (!shark->transfer_buffer)
|
||||
goto err_alloc_buffer;
|
||||
|
||||
/*
|
||||
* Work around a bug in usbhid/hid-core.c, where it leaves a dangling
|
||||
* pointer in intfdata causing v4l2-device.c to not set it. Which
|
||||
* results in usb_shark_disconnect() referencing the dangling pointer
|
||||
*
|
||||
* REMOVE (as soon as the above bug is fixed, patch submitted)
|
||||
*/
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
shark->v4l2_dev.release = usb_shark_release;
|
||||
v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
|
||||
retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
|
||||
if (retval) {
|
||||
v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
|
||||
goto err_reg_dev;
|
||||
}
|
||||
|
||||
shark->usbdev = interface_to_usbdev(intf);
|
||||
shark->tea.v4l2_dev = &shark->v4l2_dev;
|
||||
shark->tea.private_data = shark;
|
||||
shark->tea.ops = &shark_tea_ops;
|
||||
shark->tea.has_am = true;
|
||||
shark->tea.write_before_read = true;
|
||||
strlcpy(shark->tea.card, "Griffin radioSHARK2",
|
||||
sizeof(shark->tea.card));
|
||||
usb_make_path(shark->usbdev, shark->tea.bus_info,
|
||||
sizeof(shark->tea.bus_info));
|
||||
|
||||
retval = radio_tea5777_init(&shark->tea, THIS_MODULE);
|
||||
if (retval) {
|
||||
v4l2_err(&shark->v4l2_dev, "couldn't init tea5777\n");
|
||||
goto err_init_tea;
|
||||
}
|
||||
|
||||
INIT_WORK(&shark->led_work, shark_led_work);
|
||||
for (i = 0; i < NO_LEDS; i++) {
|
||||
shark->leds[i] = shark_led_templates[i];
|
||||
snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
|
||||
shark->leds[i].name, shark->v4l2_dev.name);
|
||||
shark->leds[i].name = shark->led_names[i];
|
||||
/*
|
||||
* We don't fail the probe if we fail to register the leds,
|
||||
* because once we've called radio_tea5777_init, the /dev/radio0
|
||||
* node may be opened from userspace holding a reference to us!
|
||||
*
|
||||
* Note we cannot register the leds first instead as
|
||||
* shark_led_work depends on the v4l2 mutex and registered bit.
|
||||
*/
|
||||
retval = led_classdev_register(&intf->dev, &shark->leds[i]);
|
||||
if (retval)
|
||||
v4l2_err(&shark->v4l2_dev,
|
||||
"couldn't register led: %s\n",
|
||||
shark->led_names[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_init_tea:
|
||||
v4l2_device_unregister(&shark->v4l2_dev);
|
||||
err_reg_dev:
|
||||
kfree(shark->transfer_buffer);
|
||||
err_alloc_buffer:
|
||||
kfree(shark);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
|
||||
static struct usb_device_id usb_shark_device_table[] = {
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
|
||||
USB_DEVICE_ID_MATCH_INT_CLASS,
|
||||
.idVendor = 0x077d,
|
||||
.idProduct = 0x627a,
|
||||
.bcdDevice_lo = 0x0010,
|
||||
.bcdDevice_hi = 0x0010,
|
||||
.bInterfaceClass = 3,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
|
||||
|
||||
static struct usb_driver usb_shark_driver = {
|
||||
.name = DRV_NAME,
|
||||
.probe = usb_shark_probe,
|
||||
.disconnect = usb_shark_disconnect,
|
||||
.id_table = usb_shark_device_table,
|
||||
};
|
||||
module_usb_driver(usb_shark_driver);
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
|
||||
*
|
||||
* Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
|
||||
*
|
||||
* Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <asm/div64.h>
|
||||
#include "radio-tea5777.h"
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <perex@perex.cz>");
|
||||
MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* Fixed FM only band for now, will implement multi-band support when the
|
||||
VIDIOC_ENUM_FREQ_BANDS API is upstream */
|
||||
#define TEA5777_FM_RANGELOW (76000 * 16)
|
||||
#define TEA5777_FM_RANGEHIGH (108000 * 16)
|
||||
|
||||
#define TEA5777_FM_IF 150 /* kHz */
|
||||
#define TEA5777_FM_FREQ_STEP 50 /* kHz */
|
||||
|
||||
/* Write reg, common bits */
|
||||
#define TEA5777_W_MUTE_MASK (1LL << 47)
|
||||
#define TEA5777_W_MUTE_SHIFT 47
|
||||
#define TEA5777_W_AM_FM_MASK (1LL << 46)
|
||||
#define TEA5777_W_AM_FM_SHIFT 46
|
||||
#define TEA5777_W_STB_MASK (1LL << 45)
|
||||
#define TEA5777_W_STB_SHIFT 45
|
||||
|
||||
#define TEA5777_W_IFCE_MASK (1LL << 29)
|
||||
#define TEA5777_W_IFCE_SHIFT 29
|
||||
#define TEA5777_W_IFW_MASK (1LL << 28)
|
||||
#define TEA5777_W_IFW_SHIFT 28
|
||||
#define TEA5777_W_HILO_MASK (1LL << 27)
|
||||
#define TEA5777_W_HILO_SHIFT 27
|
||||
#define TEA5777_W_DBUS_MASK (1LL << 26)
|
||||
#define TEA5777_W_DBUS_SHIFT 26
|
||||
|
||||
#define TEA5777_W_INTEXT_MASK (1LL << 24)
|
||||
#define TEA5777_W_INTEXT_SHIFT 24
|
||||
#define TEA5777_W_P1_MASK (1LL << 23)
|
||||
#define TEA5777_W_P1_SHIFT 23
|
||||
#define TEA5777_W_P0_MASK (1LL << 22)
|
||||
#define TEA5777_W_P0_SHIFT 22
|
||||
#define TEA5777_W_PEN1_MASK (1LL << 21)
|
||||
#define TEA5777_W_PEN1_SHIFT 21
|
||||
#define TEA5777_W_PEN0_MASK (1LL << 20)
|
||||
#define TEA5777_W_PEN0_SHIFT 20
|
||||
|
||||
#define TEA5777_W_CHP0_MASK (1LL << 18)
|
||||
#define TEA5777_W_CHP0_SHIFT 18
|
||||
#define TEA5777_W_DEEM_MASK (1LL << 17)
|
||||
#define TEA5777_W_DEEM_SHIFT 17
|
||||
|
||||
#define TEA5777_W_SEARCH_MASK (1LL << 7)
|
||||
#define TEA5777_W_SEARCH_SHIFT 7
|
||||
#define TEA5777_W_PROGBLIM_MASK (1LL << 6)
|
||||
#define TEA5777_W_PROGBLIM_SHIFT 6
|
||||
#define TEA5777_W_UPDWN_MASK (1LL << 5)
|
||||
#define TEA5777_W_UPDWN_SHIFT 5
|
||||
#define TEA5777_W_SLEV_MASK (3LL << 3)
|
||||
#define TEA5777_W_SLEV_SHIFT 3
|
||||
|
||||
/* Write reg, FM specific bits */
|
||||
#define TEA5777_W_FM_PLL_MASK (0x1fffLL << 32)
|
||||
#define TEA5777_W_FM_PLL_SHIFT 32
|
||||
#define TEA5777_W_FM_FREF_MASK (0x03LL << 30)
|
||||
#define TEA5777_W_FM_FREF_SHIFT 30
|
||||
#define TEA5777_W_FM_FREF_VALUE 0 /* 50 kHz tune steps, 150 kHz IF */
|
||||
|
||||
#define TEA5777_W_FM_FORCEMONO_MASK (1LL << 15)
|
||||
#define TEA5777_W_FM_FORCEMONO_SHIFT 15
|
||||
#define TEA5777_W_FM_SDSOFF_MASK (1LL << 14)
|
||||
#define TEA5777_W_FM_SDSOFF_SHIFT 14
|
||||
#define TEA5777_W_FM_DOFF_MASK (1LL << 13)
|
||||
#define TEA5777_W_FM_DOFF_SHIFT 13
|
||||
|
||||
#define TEA5777_W_FM_STEP_MASK (3LL << 1)
|
||||
#define TEA5777_W_FM_STEP_SHIFT 1
|
||||
|
||||
/* Write reg, AM specific bits */
|
||||
#define TEA5777_W_AM_PLL_MASK (0x7ffLL << 34)
|
||||
#define TEA5777_W_AM_PLL_SHIFT 34
|
||||
#define TEA5777_W_AM_AGCRF_MASK (1LL << 33)
|
||||
#define TEA5777_W_AM_AGCRF_SHIFT 33
|
||||
#define TEA5777_W_AM_AGCIF_MASK (1LL << 32)
|
||||
#define TEA5777_W_AM_AGCIF_SHIFT 32
|
||||
#define TEA5777_W_AM_MWLW_MASK (1LL << 31)
|
||||
#define TEA5777_W_AM_MWLW_SHIFT 31
|
||||
#define TEA5777_W_AM_LNA_MASK (1LL << 30)
|
||||
#define TEA5777_W_AM_LNA_SHIFT 30
|
||||
|
||||
#define TEA5777_W_AM_PEAK_MASK (1LL << 25)
|
||||
#define TEA5777_W_AM_PEAK_SHIFT 25
|
||||
|
||||
#define TEA5777_W_AM_RFB_MASK (1LL << 16)
|
||||
#define TEA5777_W_AM_RFB_SHIFT 16
|
||||
#define TEA5777_W_AM_CALLIGN_MASK (1LL << 15)
|
||||
#define TEA5777_W_AM_CALLIGN_SHIFT 15
|
||||
#define TEA5777_W_AM_CBANK_MASK (0x7fLL << 8)
|
||||
#define TEA5777_W_AM_CBANK_SHIFT 8
|
||||
|
||||
#define TEA5777_W_AM_DELAY_MASK (1LL << 2)
|
||||
#define TEA5777_W_AM_DELAY_SHIFT 2
|
||||
#define TEA5777_W_AM_STEP_MASK (1LL << 1)
|
||||
#define TEA5777_W_AM_STEP_SHIFT 1
|
||||
|
||||
/* Read reg, common bits */
|
||||
#define TEA5777_R_LEVEL_MASK (0x0f << 17)
|
||||
#define TEA5777_R_LEVEL_SHIFT 17
|
||||
#define TEA5777_R_SFOUND_MASK (0x01 << 16)
|
||||
#define TEA5777_R_SFOUND_SHIFT 16
|
||||
#define TEA5777_R_BLIM_MASK (0x01 << 15)
|
||||
#define TEA5777_R_BLIM_SHIFT 15
|
||||
|
||||
/* Read reg, FM specific bits */
|
||||
#define TEA5777_R_FM_STEREO_MASK (0x01 << 21)
|
||||
#define TEA5777_R_FM_STEREO_SHIFT 21
|
||||
#define TEA5777_R_FM_PLL_MASK 0x1fff
|
||||
#define TEA5777_R_FM_PLL_SHIFT 0
|
||||
|
||||
static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq)
|
||||
{
|
||||
return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
|
||||
}
|
||||
|
||||
static int radio_tea5777_set_freq(struct radio_tea5777 *tea)
|
||||
{
|
||||
u64 freq;
|
||||
int res;
|
||||
|
||||
freq = clamp_t(u32, tea->freq,
|
||||
TEA5777_FM_RANGELOW, TEA5777_FM_RANGEHIGH) + 8;
|
||||
do_div(freq, 16); /* to kHz */
|
||||
|
||||
freq -= TEA5777_FM_IF;
|
||||
do_div(freq, TEA5777_FM_FREQ_STEP);
|
||||
|
||||
tea->write_reg &= ~(TEA5777_W_FM_PLL_MASK | TEA5777_W_FM_FREF_MASK);
|
||||
tea->write_reg |= freq << TEA5777_W_FM_PLL_SHIFT;
|
||||
tea->write_reg |= TEA5777_W_FM_FREF_VALUE << TEA5777_W_FM_FREF_SHIFT;
|
||||
|
||||
res = tea->ops->write_reg(tea, tea->write_reg);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
tea->needs_write = false;
|
||||
tea->read_reg = -1;
|
||||
tea->freq = tea5777_freq_to_v4l2_freq(tea, freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int radio_tea5777_update_read_reg(struct radio_tea5777 *tea, int wait)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (tea->read_reg != -1)
|
||||
return 0;
|
||||
|
||||
if (tea->write_before_read && tea->needs_write) {
|
||||
res = radio_tea5777_set_freq(tea);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
if (wait) {
|
||||
if (schedule_timeout_interruptible(msecs_to_jiffies(wait)))
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
res = tea->ops->read_reg(tea, &tea->read_reg);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
tea->needs_write = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux Video interface
|
||||
*/
|
||||
|
||||
static int vidioc_querycap(struct file *file, void *priv,
|
||||
struct v4l2_capability *v)
|
||||
{
|
||||
struct radio_tea5777 *tea = video_drvdata(file);
|
||||
|
||||
strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
|
||||
strlcpy(v->card, tea->card, sizeof(v->card));
|
||||
strlcat(v->card, " TEA5777", sizeof(v->card));
|
||||
strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
|
||||
v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
||||
v->device_caps |= V4L2_CAP_HW_FREQ_SEEK;
|
||||
v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_g_tuner(struct file *file, void *priv,
|
||||
struct v4l2_tuner *v)
|
||||
{
|
||||
struct radio_tea5777 *tea = video_drvdata(file);
|
||||
int res;
|
||||
|
||||
if (v->index > 0)
|
||||
return -EINVAL;
|
||||
|
||||
res = radio_tea5777_update_read_reg(tea, 0);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
memset(v, 0, sizeof(*v));
|
||||
if (tea->has_am)
|
||||
strlcpy(v->name, "AM/FM", sizeof(v->name));
|
||||
else
|
||||
strlcpy(v->name, "FM", sizeof(v->name));
|
||||
v->type = V4L2_TUNER_RADIO;
|
||||
v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
|
||||
V4L2_TUNER_CAP_HWSEEK_BOUNDED;
|
||||
v->rangelow = TEA5777_FM_RANGELOW;
|
||||
v->rangehigh = TEA5777_FM_RANGEHIGH;
|
||||
v->rxsubchans = (tea->read_reg & TEA5777_R_FM_STEREO_MASK) ?
|
||||
V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
|
||||
v->audmode = (tea->write_reg & TEA5777_W_FM_FORCEMONO_MASK) ?
|
||||
V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
|
||||
/* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */
|
||||
v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >>
|
||||
(TEA5777_R_LEVEL_SHIFT - 12);
|
||||
|
||||
/* Invalidate read_reg, so that next call we return up2date signal */
|
||||
tea->read_reg = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_tuner(struct file *file, void *priv,
|
||||
struct v4l2_tuner *v)
|
||||
{
|
||||
struct radio_tea5777 *tea = video_drvdata(file);
|
||||
|
||||
if (v->index)
|
||||
return -EINVAL;
|
||||
|
||||
if (v->audmode == V4L2_TUNER_MODE_MONO)
|
||||
tea->write_reg |= TEA5777_W_FM_FORCEMONO_MASK;
|
||||
else
|
||||
tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK;
|
||||
|
||||
return radio_tea5777_set_freq(tea);
|
||||
}
|
||||
|
||||
static int vidioc_g_frequency(struct file *file, void *priv,
|
||||
struct v4l2_frequency *f)
|
||||
{
|
||||
struct radio_tea5777 *tea = video_drvdata(file);
|
||||
|
||||
if (f->tuner != 0)
|
||||
return -EINVAL;
|
||||
f->type = V4L2_TUNER_RADIO;
|
||||
f->frequency = tea->freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidioc_s_frequency(struct file *file, void *priv,
|
||||
struct v4l2_frequency *f)
|
||||
{
|
||||
struct radio_tea5777 *tea = video_drvdata(file);
|
||||
|
||||
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
||||
return -EINVAL;
|
||||
|
||||
tea->freq = f->frequency;
|
||||
return radio_tea5777_set_freq(tea);
|
||||
}
|
||||
|
||||
static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
|
||||
struct v4l2_hw_freq_seek *a)
|
||||
{
|
||||
struct radio_tea5777 *tea = video_drvdata(file);
|
||||
u32 orig_freq = tea->freq;
|
||||
unsigned long timeout;
|
||||
int res, spacing = 200 * 16; /* 200 kHz */
|
||||
/* These are fixed *for now* */
|
||||
const u32 seek_rangelow = TEA5777_FM_RANGELOW;
|
||||
const u32 seek_rangehigh = TEA5777_FM_RANGEHIGH;
|
||||
|
||||
if (a->tuner || a->wrap_around)
|
||||
return -EINVAL;
|
||||
|
||||
tea->write_reg |= TEA5777_W_PROGBLIM_MASK;
|
||||
if (seek_rangelow != tea->seek_rangelow) {
|
||||
tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
|
||||
tea->freq = seek_rangelow;
|
||||
res = radio_tea5777_set_freq(tea);
|
||||
if (res)
|
||||
goto leave;
|
||||
tea->seek_rangelow = tea->freq;
|
||||
}
|
||||
if (seek_rangehigh != tea->seek_rangehigh) {
|
||||
tea->write_reg |= TEA5777_W_UPDWN_MASK;
|
||||
tea->freq = seek_rangehigh;
|
||||
res = radio_tea5777_set_freq(tea);
|
||||
if (res)
|
||||
goto leave;
|
||||
tea->seek_rangehigh = tea->freq;
|
||||
}
|
||||
tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
|
||||
|
||||
tea->write_reg |= TEA5777_W_SEARCH_MASK;
|
||||
if (a->seek_upward) {
|
||||
tea->write_reg |= TEA5777_W_UPDWN_MASK;
|
||||
tea->freq = orig_freq + spacing;
|
||||
} else {
|
||||
tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
|
||||
tea->freq = orig_freq - spacing;
|
||||
}
|
||||
res = radio_tea5777_set_freq(tea);
|
||||
if (res)
|
||||
goto leave;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(5000);
|
||||
for (;;) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
res = -ENODATA;
|
||||
break;
|
||||
}
|
||||
|
||||
res = radio_tea5777_update_read_reg(tea, 100);
|
||||
if (res)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Note we use tea->freq to track how far we've searched sofar
|
||||
* this is necessary to ensure we continue seeking at the right
|
||||
* point, in the write_before_read case.
|
||||
*/
|
||||
tea->freq = (tea->read_reg & TEA5777_R_FM_PLL_MASK);
|
||||
tea->freq = tea5777_freq_to_v4l2_freq(tea, tea->freq);
|
||||
|
||||
if ((tea->read_reg & TEA5777_R_SFOUND_MASK)) {
|
||||
tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tea->read_reg & TEA5777_R_BLIM_MASK) {
|
||||
res = -ENODATA;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Force read_reg update */
|
||||
tea->read_reg = -1;
|
||||
}
|
||||
leave:
|
||||
tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
|
||||
tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
|
||||
tea->freq = orig_freq;
|
||||
radio_tea5777_set_freq(tea);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int tea575x_s_ctrl(struct v4l2_ctrl *c)
|
||||
{
|
||||
struct radio_tea5777 *tea =
|
||||
container_of(c->handler, struct radio_tea5777, ctrl_handler);
|
||||
|
||||
switch (c->id) {
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
if (c->val)
|
||||
tea->write_reg |= TEA5777_W_MUTE_MASK;
|
||||
else
|
||||
tea->write_reg &= ~TEA5777_W_MUTE_MASK;
|
||||
|
||||
return radio_tea5777_set_freq(tea);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct v4l2_file_operations tea575x_fops = {
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
.open = v4l2_fh_open,
|
||||
.release = v4l2_fh_release,
|
||||
.poll = v4l2_ctrl_poll,
|
||||
};
|
||||
|
||||
static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
|
||||
.vidioc_querycap = vidioc_querycap,
|
||||
.vidioc_g_tuner = vidioc_g_tuner,
|
||||
.vidioc_s_tuner = vidioc_s_tuner,
|
||||
.vidioc_g_frequency = vidioc_g_frequency,
|
||||
.vidioc_s_frequency = vidioc_s_frequency,
|
||||
.vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
|
||||
.vidioc_log_status = v4l2_ctrl_log_status,
|
||||
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
||||
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
||||
};
|
||||
|
||||
static const struct video_device tea575x_radio = {
|
||||
.ioctl_ops = &tea575x_ioctl_ops,
|
||||
.release = video_device_release_empty,
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
|
||||
.s_ctrl = tea575x_s_ctrl,
|
||||
};
|
||||
|
||||
int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner)
|
||||
{
|
||||
int res;
|
||||
|
||||
tea->write_reg = (1LL << TEA5777_W_IFCE_SHIFT) |
|
||||
(1LL << TEA5777_W_IFW_SHIFT) |
|
||||
(1LL << TEA5777_W_INTEXT_SHIFT) |
|
||||
(1LL << TEA5777_W_CHP0_SHIFT) |
|
||||
(2LL << TEA5777_W_SLEV_SHIFT);
|
||||
tea->freq = 90500 * 16; /* 90.5Mhz default */
|
||||
res = radio_tea5777_set_freq(tea);
|
||||
if (res) {
|
||||
v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
tea->vd = tea575x_radio;
|
||||
video_set_drvdata(&tea->vd, tea);
|
||||
mutex_init(&tea->mutex);
|
||||
strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
|
||||
tea->vd.lock = &tea->mutex;
|
||||
tea->vd.v4l2_dev = tea->v4l2_dev;
|
||||
tea->fops = tea575x_fops;
|
||||
tea->fops.owner = owner;
|
||||
tea->vd.fops = &tea->fops;
|
||||
set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
|
||||
|
||||
tea->vd.ctrl_handler = &tea->ctrl_handler;
|
||||
v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
|
||||
v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
|
||||
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
|
||||
res = tea->ctrl_handler.error;
|
||||
if (res) {
|
||||
v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
|
||||
v4l2_ctrl_handler_free(&tea->ctrl_handler);
|
||||
return res;
|
||||
}
|
||||
v4l2_ctrl_handler_setup(&tea->ctrl_handler);
|
||||
|
||||
res = video_register_device(&tea->vd, VFL_TYPE_RADIO, -1);
|
||||
if (res) {
|
||||
v4l2_err(tea->v4l2_dev, "can't register video device!\n");
|
||||
v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(radio_tea5777_init);
|
||||
|
||||
void radio_tea5777_exit(struct radio_tea5777 *tea)
|
||||
{
|
||||
video_unregister_device(&tea->vd);
|
||||
v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(radio_tea5777_exit);
|
|
@ -0,0 +1,87 @@
|
|||
#ifndef __RADIO_TEA5777_H
|
||||
#define __RADIO_TEA5777_H
|
||||
|
||||
/*
|
||||
* v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
|
||||
*
|
||||
* Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
|
||||
*
|
||||
* Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
|
||||
* Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
|
||||
#define TEA575X_FMIF 10700
|
||||
#define TEA575X_AMIF 450
|
||||
|
||||
struct radio_tea5777;
|
||||
|
||||
struct radio_tea5777_ops {
|
||||
/*
|
||||
* Write the 6 bytes large write register of the tea5777
|
||||
*
|
||||
* val represents the 6 write registers, with byte 1 from the
|
||||
* datasheet being the most significant byte (so byte 5 of the u64),
|
||||
* and byte 6 from the datasheet being the least significant byte.
|
||||
*
|
||||
* returns 0 on success.
|
||||
*/
|
||||
int (*write_reg)(struct radio_tea5777 *tea, u64 val);
|
||||
/*
|
||||
* Read the 3 bytes large read register of the tea5777
|
||||
*
|
||||
* The read value gets returned in val, akin to write_reg, byte 1 from
|
||||
* the datasheet is stored as the most significant byte (so byte 2 of
|
||||
* the u32), and byte 3 from the datasheet gets stored as the least
|
||||
* significant byte (iow byte 0 of the u32).
|
||||
*
|
||||
* returns 0 on success.
|
||||
*/
|
||||
int (*read_reg)(struct radio_tea5777 *tea, u32 *val);
|
||||
};
|
||||
|
||||
struct radio_tea5777 {
|
||||
struct v4l2_device *v4l2_dev;
|
||||
struct v4l2_file_operations fops;
|
||||
struct video_device vd; /* video device */
|
||||
bool has_am; /* Device can tune to AM freqs */
|
||||
bool write_before_read; /* must write before read quirk */
|
||||
bool needs_write; /* for write before read quirk */
|
||||
u32 freq; /* current frequency */
|
||||
u32 seek_rangelow; /* current hwseek limits */
|
||||
u32 seek_rangehigh;
|
||||
u32 read_reg;
|
||||
u64 write_reg;
|
||||
struct mutex mutex;
|
||||
struct radio_tea5777_ops *ops;
|
||||
void *private_data;
|
||||
u8 card[32];
|
||||
u8 bus_info[32];
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
};
|
||||
|
||||
int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner);
|
||||
void radio_tea5777_exit(struct radio_tea5777 *tea);
|
||||
|
||||
#endif /* __RADIO_TEA5777_H */
|
|
@ -4,6 +4,7 @@
|
|||
* Driver for radios with Silicon Labs Si470x FM Radio Receivers
|
||||
*
|
||||
* Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
|
||||
* Copyright (c) 2012 Hans de Goede <hdegoede@redhat.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
|
||||
|
@ -127,14 +128,6 @@ static unsigned short space = 2;
|
|||
module_param(space, ushort, 0444);
|
||||
MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
|
||||
|
||||
/* Bottom of Band (MHz) */
|
||||
/* 0: 87.5 - 108 MHz (USA, Europe)*/
|
||||
/* 1: 76 - 108 MHz (Japan wide band) */
|
||||
/* 2: 76 - 90 MHz (Japan) */
|
||||
static unsigned short band = 1;
|
||||
module_param(band, ushort, 0444);
|
||||
MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
|
||||
|
||||
/* De-emphasis */
|
||||
/* 0: 75 us (USA) */
|
||||
/* 1: 50 us (Europe, Australia, Japan) */
|
||||
|
@ -152,19 +145,66 @@ static unsigned int seek_timeout = 5000;
|
|||
module_param(seek_timeout, uint, 0644);
|
||||
MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
|
||||
|
||||
|
||||
static const struct v4l2_frequency_band bands[] = {
|
||||
{
|
||||
.type = V4L2_TUNER_RADIO,
|
||||
.index = 0,
|
||||
.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
|
||||
V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
|
||||
V4L2_TUNER_CAP_HWSEEK_BOUNDED |
|
||||
V4L2_TUNER_CAP_HWSEEK_WRAP,
|
||||
.rangelow = 87500 * 16,
|
||||
.rangehigh = 108000 * 16,
|
||||
.modulation = V4L2_BAND_MODULATION_FM,
|
||||
},
|
||||
{
|
||||
.type = V4L2_TUNER_RADIO,
|
||||
.index = 1,
|
||||
.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
|
||||
V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
|
||||
V4L2_TUNER_CAP_HWSEEK_BOUNDED |
|
||||
V4L2_TUNER_CAP_HWSEEK_WRAP,
|
||||
.rangelow = 76000 * 16,
|
||||
.rangehigh = 108000 * 16,
|
||||
.modulation = V4L2_BAND_MODULATION_FM,
|
||||
},
|
||||
{
|
||||
.type = V4L2_TUNER_RADIO,
|
||||
.index = 2,
|
||||
.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
|
||||
V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
|
||||
V4L2_TUNER_CAP_HWSEEK_BOUNDED |
|
||||
V4L2_TUNER_CAP_HWSEEK_WRAP,
|
||||
.rangelow = 76000 * 16,
|
||||
.rangehigh = 90000 * 16,
|
||||
.modulation = V4L2_BAND_MODULATION_FM,
|
||||
},
|
||||
};
|
||||
|
||||
/**************************************************************************
|
||||
* Generic Functions
|
||||
**************************************************************************/
|
||||
|
||||
/*
|
||||
* si470x_set_band - set the band
|
||||
*/
|
||||
static int si470x_set_band(struct si470x_device *radio, int band)
|
||||
{
|
||||
if (radio->band == band)
|
||||
return 0;
|
||||
|
||||
radio->band = band;
|
||||
radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
|
||||
radio->registers[SYSCONFIG2] |= radio->band << 6;
|
||||
return si470x_set_register(radio, SYSCONFIG2);
|
||||
}
|
||||
|
||||
/*
|
||||
* si470x_set_chan - set the channel
|
||||
*/
|
||||
static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
|
||||
{
|
||||
int retval;
|
||||
unsigned long timeout;
|
||||
bool timed_out = 0;
|
||||
|
||||
/* start tuning */
|
||||
|
@ -174,26 +214,12 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
|
|||
if (retval < 0)
|
||||
goto done;
|
||||
|
||||
/* currently I2C driver only uses interrupt way to tune */
|
||||
if (radio->stci_enabled) {
|
||||
INIT_COMPLETION(radio->completion);
|
||||
|
||||
/* wait till tune operation has completed */
|
||||
retval = wait_for_completion_timeout(&radio->completion,
|
||||
msecs_to_jiffies(tune_timeout));
|
||||
if (!retval)
|
||||
timed_out = true;
|
||||
} else {
|
||||
/* wait till tune operation has completed */
|
||||
timeout = jiffies + msecs_to_jiffies(tune_timeout);
|
||||
do {
|
||||
retval = si470x_get_register(radio, STATUSRSSI);
|
||||
if (retval < 0)
|
||||
goto stop;
|
||||
timed_out = time_after(jiffies, timeout);
|
||||
} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
|
||||
&& (!timed_out));
|
||||
}
|
||||
/* wait till tune operation has completed */
|
||||
INIT_COMPLETION(radio->completion);
|
||||
retval = wait_for_completion_timeout(&radio->completion,
|
||||
msecs_to_jiffies(tune_timeout));
|
||||
if (!retval)
|
||||
timed_out = true;
|
||||
|
||||
if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
|
||||
dev_warn(&radio->videodev.dev, "tune does not complete\n");
|
||||
|
@ -201,7 +227,6 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
|
|||
dev_warn(&radio->videodev.dev,
|
||||
"tune timed out after %u ms\n", tune_timeout);
|
||||
|
||||
stop:
|
||||
/* stop tuning */
|
||||
radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
|
||||
retval = si470x_set_register(radio, CHANNEL);
|
||||
|
@ -210,48 +235,39 @@ done:
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* si470x_get_step - get channel spacing
|
||||
*/
|
||||
static unsigned int si470x_get_step(struct si470x_device *radio)
|
||||
{
|
||||
/* Spacing (kHz) */
|
||||
switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
|
||||
/* 0: 200 kHz (USA, Australia) */
|
||||
case 0:
|
||||
return 200 * 16;
|
||||
/* 1: 100 kHz (Europe, Japan) */
|
||||
case 1:
|
||||
return 100 * 16;
|
||||
/* 2: 50 kHz */
|
||||
default:
|
||||
return 50 * 16;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* si470x_get_freq - get the frequency
|
||||
*/
|
||||
static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
|
||||
{
|
||||
unsigned int spacing, band_bottom;
|
||||
unsigned short chan;
|
||||
int retval;
|
||||
|
||||
/* Spacing (kHz) */
|
||||
switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
|
||||
/* 0: 200 kHz (USA, Australia) */
|
||||
case 0:
|
||||
spacing = 0.200 * FREQ_MUL; break;
|
||||
/* 1: 100 kHz (Europe, Japan) */
|
||||
case 1:
|
||||
spacing = 0.100 * FREQ_MUL; break;
|
||||
/* 2: 50 kHz */
|
||||
default:
|
||||
spacing = 0.050 * FREQ_MUL; break;
|
||||
};
|
||||
|
||||
/* Bottom of Band (MHz) */
|
||||
switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
|
||||
/* 0: 87.5 - 108 MHz (USA, Europe) */
|
||||
case 0:
|
||||
band_bottom = 87.5 * FREQ_MUL; break;
|
||||
/* 1: 76 - 108 MHz (Japan wide band) */
|
||||
default:
|
||||
band_bottom = 76 * FREQ_MUL; break;
|
||||
/* 2: 76 - 90 MHz (Japan) */
|
||||
case 2:
|
||||
band_bottom = 76 * FREQ_MUL; break;
|
||||
};
|
||||
int chan, retval;
|
||||
|
||||
/* read channel */
|
||||
retval = si470x_get_register(radio, READCHAN);
|
||||
chan = radio->registers[READCHAN] & READCHAN_READCHAN;
|
||||
|
||||
/* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
|
||||
*freq = chan * spacing + band_bottom;
|
||||
*freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -262,44 +278,12 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
|
|||
*/
|
||||
int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
|
||||
{
|
||||
unsigned int spacing, band_bottom, band_top;
|
||||
unsigned short chan;
|
||||
|
||||
/* Spacing (kHz) */
|
||||
switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
|
||||
/* 0: 200 kHz (USA, Australia) */
|
||||
case 0:
|
||||
spacing = 0.200 * FREQ_MUL; break;
|
||||
/* 1: 100 kHz (Europe, Japan) */
|
||||
case 1:
|
||||
spacing = 0.100 * FREQ_MUL; break;
|
||||
/* 2: 50 kHz */
|
||||
default:
|
||||
spacing = 0.050 * FREQ_MUL; break;
|
||||
};
|
||||
|
||||
/* Bottom/Top of Band (MHz) */
|
||||
switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
|
||||
/* 0: 87.5 - 108 MHz (USA, Europe) */
|
||||
case 0:
|
||||
band_bottom = 87.5 * FREQ_MUL;
|
||||
band_top = 108 * FREQ_MUL;
|
||||
break;
|
||||
/* 1: 76 - 108 MHz (Japan wide band) */
|
||||
default:
|
||||
band_bottom = 76 * FREQ_MUL;
|
||||
band_top = 108 * FREQ_MUL;
|
||||
break;
|
||||
/* 2: 76 - 90 MHz (Japan) */
|
||||
case 2:
|
||||
band_bottom = 76 * FREQ_MUL;
|
||||
band_top = 90 * FREQ_MUL;
|
||||
break;
|
||||
};
|
||||
|
||||
freq = clamp(freq, band_bottom, band_top);
|
||||
freq = clamp(freq, bands[radio->band].rangelow,
|
||||
bands[radio->band].rangehigh);
|
||||
/* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
|
||||
chan = (freq - band_bottom) / spacing;
|
||||
chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio);
|
||||
|
||||
return si470x_set_chan(radio, chan);
|
||||
}
|
||||
|
@ -309,19 +293,43 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
|
|||
* si470x_set_seek - set seek
|
||||
*/
|
||||
static int si470x_set_seek(struct si470x_device *radio,
|
||||
unsigned int wrap_around, unsigned int seek_upward)
|
||||
struct v4l2_hw_freq_seek *seek)
|
||||
{
|
||||
int retval = 0;
|
||||
unsigned long timeout;
|
||||
int band, retval;
|
||||
unsigned int freq;
|
||||
bool timed_out = 0;
|
||||
|
||||
/* set band */
|
||||
if (seek->rangelow || seek->rangehigh) {
|
||||
for (band = 0; band < ARRAY_SIZE(bands); band++) {
|
||||
if (bands[band].rangelow == seek->rangelow &&
|
||||
bands[band].rangehigh == seek->rangehigh)
|
||||
break;
|
||||
}
|
||||
if (band == ARRAY_SIZE(bands))
|
||||
return -EINVAL; /* No matching band found */
|
||||
} else
|
||||
band = 1; /* If nothing is specified seek 76 - 108 Mhz */
|
||||
|
||||
if (radio->band != band) {
|
||||
retval = si470x_get_freq(radio, &freq);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = si470x_set_band(radio, band);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = si470x_set_freq(radio, freq);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* start seeking */
|
||||
radio->registers[POWERCFG] |= POWERCFG_SEEK;
|
||||
if (wrap_around == 1)
|
||||
if (seek->wrap_around)
|
||||
radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
|
||||
else
|
||||
radio->registers[POWERCFG] |= POWERCFG_SKMODE;
|
||||
if (seek_upward == 1)
|
||||
if (seek->seek_upward)
|
||||
radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
|
||||
else
|
||||
radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
|
||||
|
@ -329,26 +337,12 @@ static int si470x_set_seek(struct si470x_device *radio,
|
|||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
/* currently I2C driver only uses interrupt way to seek */
|
||||
if (radio->stci_enabled) {
|
||||
INIT_COMPLETION(radio->completion);
|
||||
|
||||
/* wait till seek operation has completed */
|
||||
retval = wait_for_completion_timeout(&radio->completion,
|
||||
msecs_to_jiffies(seek_timeout));
|
||||
if (!retval)
|
||||
timed_out = true;
|
||||
} else {
|
||||
/* wait till seek operation has completed */
|
||||
timeout = jiffies + msecs_to_jiffies(seek_timeout);
|
||||
do {
|
||||
retval = si470x_get_register(radio, STATUSRSSI);
|
||||
if (retval < 0)
|
||||
goto stop;
|
||||
timed_out = time_after(jiffies, timeout);
|
||||
} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
|
||||
&& (!timed_out));
|
||||
}
|
||||
/* wait till tune operation has completed */
|
||||
INIT_COMPLETION(radio->completion);
|
||||
retval = wait_for_completion_timeout(&radio->completion,
|
||||
msecs_to_jiffies(seek_timeout));
|
||||
if (!retval)
|
||||
timed_out = true;
|
||||
|
||||
if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
|
||||
dev_warn(&radio->videodev.dev, "seek does not complete\n");
|
||||
|
@ -356,7 +350,6 @@ static int si470x_set_seek(struct si470x_device *radio,
|
|||
dev_warn(&radio->videodev.dev,
|
||||
"seek failed / band limit reached\n");
|
||||
|
||||
stop:
|
||||
/* stop seeking */
|
||||
radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
|
||||
retval = si470x_set_register(radio, POWERCFG);
|
||||
|
@ -391,8 +384,8 @@ int si470x_start(struct si470x_device *radio)
|
|||
|
||||
/* sysconfig 2 */
|
||||
radio->registers[SYSCONFIG2] =
|
||||
(0x3f << 8) | /* SEEKTH */
|
||||
((band << 6) & SYSCONFIG2_BAND) | /* BAND */
|
||||
(0x1f << 8) | /* SEEKTH */
|
||||
((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
|
||||
((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
|
||||
15; /* VOLUME (max) */
|
||||
retval = si470x_set_register(radio, SYSCONFIG2);
|
||||
|
@ -583,14 +576,16 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
|
|||
struct v4l2_tuner *tuner)
|
||||
{
|
||||
struct si470x_device *radio = video_drvdata(file);
|
||||
int retval;
|
||||
int retval = 0;
|
||||
|
||||
if (tuner->index != 0)
|
||||
return -EINVAL;
|
||||
|
||||
retval = si470x_get_register(radio, STATUSRSSI);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
if (!radio->status_rssi_auto_update) {
|
||||
retval = si470x_get_register(radio, STATUSRSSI);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* driver constants */
|
||||
strcpy(tuner->name, "FM");
|
||||
|
@ -599,25 +594,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
|
|||
V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
|
||||
V4L2_TUNER_CAP_HWSEEK_BOUNDED |
|
||||
V4L2_TUNER_CAP_HWSEEK_WRAP;
|
||||
|
||||
/* range limits */
|
||||
switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
|
||||
/* 0: 87.5 - 108 MHz (USA, Europe, default) */
|
||||
default:
|
||||
tuner->rangelow = 87.5 * FREQ_MUL;
|
||||
tuner->rangehigh = 108 * FREQ_MUL;
|
||||
break;
|
||||
/* 1: 76 - 108 MHz (Japan wide band) */
|
||||
case 1:
|
||||
tuner->rangelow = 76 * FREQ_MUL;
|
||||
tuner->rangehigh = 108 * FREQ_MUL;
|
||||
break;
|
||||
/* 2: 76 - 90 MHz (Japan) */
|
||||
case 2:
|
||||
tuner->rangelow = 76 * FREQ_MUL;
|
||||
tuner->rangehigh = 90 * FREQ_MUL;
|
||||
break;
|
||||
};
|
||||
tuner->rangelow = 76 * FREQ_MUL;
|
||||
tuner->rangehigh = 108 * FREQ_MUL;
|
||||
|
||||
/* stereo indicator == stereo (instead of mono) */
|
||||
if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
|
||||
|
@ -700,10 +678,18 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
|
|||
struct v4l2_frequency *freq)
|
||||
{
|
||||
struct si470x_device *radio = video_drvdata(file);
|
||||
int retval;
|
||||
|
||||
if (freq->tuner != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (freq->frequency < bands[radio->band].rangelow ||
|
||||
freq->frequency > bands[radio->band].rangehigh) {
|
||||
/* Switch to band 1 which covers everything we support */
|
||||
retval = si470x_set_band(radio, 1);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
return si470x_set_freq(radio, freq->frequency);
|
||||
}
|
||||
|
||||
|
@ -719,7 +705,21 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
|
|||
if (seek->tuner != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
|
||||
return si470x_set_seek(radio, seek);
|
||||
}
|
||||
|
||||
/*
|
||||
* si470x_vidioc_enum_freq_bands - enumerate supported bands
|
||||
*/
|
||||
static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
|
||||
struct v4l2_frequency_band *band)
|
||||
{
|
||||
if (band->tuner != 0)
|
||||
return -EINVAL;
|
||||
if (band->index >= ARRAY_SIZE(bands))
|
||||
return -EINVAL;
|
||||
*band = bands[band->index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct v4l2_ctrl_ops si470x_ctrl_ops = {
|
||||
|
@ -736,6 +736,7 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
|
|||
.vidioc_g_frequency = si470x_vidioc_g_frequency,
|
||||
.vidioc_s_frequency = si470x_vidioc_s_frequency,
|
||||
.vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
|
||||
.vidioc_enum_freq_bands = si470x_vidioc_enum_freq_bands,
|
||||
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
||||
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
||||
};
|
||||
|
|
|
@ -350,7 +350,9 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
radio->client = client;
|
||||
radio->band = 1; /* Default to 76 - 108 MHz */
|
||||
mutex_init(&radio->lock);
|
||||
init_completion(&radio->completion);
|
||||
|
||||
/* video device initialization */
|
||||
radio->videodev = si470x_viddev_template;
|
||||
|
@ -406,10 +408,6 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
|
|||
radio->rd_index = 0;
|
||||
init_waitqueue_head(&radio->read_queue);
|
||||
|
||||
/* mark Seek/Tune Complete Interrupt enabled */
|
||||
radio->stci_enabled = true;
|
||||
init_completion(&radio->completion);
|
||||
|
||||
retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt,
|
||||
IRQF_TRIGGER_FALLING, DRIVER_NAME, radio);
|
||||
if (retval) {
|
||||
|
|
|
@ -143,7 +143,7 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
|
|||
* Software/Hardware Versions from Scratch Page
|
||||
**************************************************************************/
|
||||
#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6
|
||||
#define RADIO_SW_VERSION 7
|
||||
#define RADIO_SW_VERSION 1
|
||||
#define RADIO_HW_VERSION 1
|
||||
|
||||
|
||||
|
@ -399,12 +399,19 @@ static void si470x_int_in_callback(struct urb *urb)
|
|||
}
|
||||
}
|
||||
|
||||
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
|
||||
/* Sometimes the device returns len 0 packets */
|
||||
if (urb->actual_length != RDS_REPORT_SIZE)
|
||||
goto resubmit;
|
||||
|
||||
if (urb->actual_length > 0) {
|
||||
radio->registers[STATUSRSSI] =
|
||||
get_unaligned_be16(&radio->int_in_buffer[1]);
|
||||
|
||||
if (radio->registers[STATUSRSSI] & STATUSRSSI_STC)
|
||||
complete(&radio->completion);
|
||||
|
||||
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS)) {
|
||||
/* Update RDS registers with URB data */
|
||||
for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
|
||||
for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++)
|
||||
radio->registers[STATUSRSSI + regnr] =
|
||||
get_unaligned_be16(&radio->int_in_buffer[
|
||||
regnr * RADIO_REGISTER_SIZE + 1]);
|
||||
|
@ -480,6 +487,7 @@ resubmit:
|
|||
radio->int_in_running = 0;
|
||||
}
|
||||
}
|
||||
radio->status_rssi_auto_update = radio->int_in_running;
|
||||
}
|
||||
|
||||
|
||||
|
@ -534,13 +542,6 @@ static int si470x_start_usb(struct si470x_device *radio)
|
|||
{
|
||||
int retval;
|
||||
|
||||
/* start radio */
|
||||
retval = si470x_start(radio);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
v4l2_ctrl_handler_setup(&radio->hdl);
|
||||
|
||||
/* initialize interrupt urb */
|
||||
usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
|
||||
usb_rcvintpipe(radio->usbdev,
|
||||
|
@ -560,6 +561,15 @@ static int si470x_start_usb(struct si470x_device *radio)
|
|||
"submitting int urb failed (%d)\n", retval);
|
||||
radio->int_in_running = 0;
|
||||
}
|
||||
radio->status_rssi_auto_update = radio->int_in_running;
|
||||
|
||||
/* start radio */
|
||||
retval = si470x_start(radio);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
v4l2_ctrl_handler_setup(&radio->hdl);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -587,7 +597,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
|
|||
}
|
||||
radio->usbdev = interface_to_usbdev(intf);
|
||||
radio->intf = intf;
|
||||
radio->band = 1; /* Default to 76 - 108 MHz */
|
||||
mutex_init(&radio->lock);
|
||||
init_completion(&radio->completion);
|
||||
|
||||
iface_desc = intf->cur_altsetting;
|
||||
|
||||
|
@ -698,9 +710,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
|
|||
"linux-media@vger.kernel.org\n");
|
||||
}
|
||||
|
||||
/* set initial frequency */
|
||||
si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
|
||||
|
||||
/* set led to connect state */
|
||||
si470x_set_led_state(radio, BLINK_GREEN_LED);
|
||||
|
||||
|
@ -723,6 +732,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
|
|||
if (retval < 0)
|
||||
goto err_all;
|
||||
|
||||
/* set initial frequency */
|
||||
si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
|
||||
|
||||
/* register video device */
|
||||
retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
|
||||
radio_nr);
|
||||
|
@ -781,11 +793,16 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf,
|
|||
static int si470x_usb_driver_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct si470x_device *radio = usb_get_intfdata(intf);
|
||||
int ret;
|
||||
|
||||
dev_info(&intf->dev, "resuming now...\n");
|
||||
|
||||
/* start radio */
|
||||
return si470x_start_usb(radio);
|
||||
ret = si470x_start_usb(radio);
|
||||
if (ret == 0)
|
||||
v4l2_ctrl_handler_setup(&radio->hdl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
|
||||
#define SYSCONFIG2 5 /* System Configuration 2 */
|
||||
#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */
|
||||
#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */
|
||||
#define SYSCONFIG2_BAND 0x00c0 /* bits 07..06: Band Select */
|
||||
#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */
|
||||
#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */
|
||||
|
||||
|
@ -147,6 +147,7 @@ struct si470x_device {
|
|||
struct v4l2_device v4l2_dev;
|
||||
struct video_device videodev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
int band;
|
||||
|
||||
/* Silabs internal registers (0..15) */
|
||||
unsigned short registers[RADIO_REGISTER_NUM];
|
||||
|
@ -160,7 +161,7 @@ struct si470x_device {
|
|||
unsigned int wr_index;
|
||||
|
||||
struct completion completion;
|
||||
bool stci_enabled; /* Seek/Tune Complete Interrupt */
|
||||
bool status_rssi_auto_update; /* Does RSSI get updated automatic? */
|
||||
|
||||
#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
|
||||
/* reference to USB and video device */
|
||||
|
@ -189,7 +190,7 @@ struct si470x_device {
|
|||
* Firmware Versions
|
||||
**************************************************************************/
|
||||
|
||||
#define RADIO_FW_VERSION 15
|
||||
#define RADIO_FW_VERSION 12
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -259,6 +259,17 @@ config IR_WINBOND_CIR
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called winbond_cir.
|
||||
|
||||
config IR_IGUANA
|
||||
tristate "IguanaWorks USB IR Transceiver"
|
||||
depends on RC_CORE
|
||||
select USB
|
||||
---help---
|
||||
Say Y here if you want to use the IgaunaWorks USB IR Transceiver.
|
||||
Both infrared receive and send are supported.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called iguanair.
|
||||
|
||||
config RC_LOOPBACK
|
||||
tristate "Remote Control Loopback Driver"
|
||||
depends on RC_CORE
|
||||
|
|
|
@ -27,3 +27,4 @@ obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
|
|||
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
|
||||
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
|
||||
obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
|
||||
obj-$(CONFIG_IR_IGUANA) += iguanair.o
|
||||
|
|
|
@ -147,7 +147,8 @@ static bool mouse = true;
|
|||
module_param(mouse, bool, 0444);
|
||||
MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes");
|
||||
|
||||
#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
|
||||
#define dbginfo(dev, format, arg...) \
|
||||
do { if (debug) dev_info(dev , format , ## arg); } while (0)
|
||||
#undef err
|
||||
#define err(format, arg...) printk(KERN_ERR format , ## arg)
|
||||
|
||||
|
@ -191,17 +192,41 @@ static const char *get_medion_keymap(struct usb_interface *interface)
|
|||
return RC_MAP_MEDION_X10;
|
||||
}
|
||||
|
||||
static const struct ati_receiver_type type_ati = { .default_keymap = RC_MAP_ATI_X10 };
|
||||
static const struct ati_receiver_type type_medion = { .get_default_keymap = get_medion_keymap };
|
||||
static const struct ati_receiver_type type_firefly = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY };
|
||||
static const struct ati_receiver_type type_ati = {
|
||||
.default_keymap = RC_MAP_ATI_X10
|
||||
};
|
||||
static const struct ati_receiver_type type_medion = {
|
||||
.get_default_keymap = get_medion_keymap
|
||||
};
|
||||
static const struct ati_receiver_type type_firefly = {
|
||||
.default_keymap = RC_MAP_SNAPSTREAM_FIREFLY
|
||||
};
|
||||
|
||||
static struct usb_device_id ati_remote_table[] = {
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_medion },
|
||||
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_firefly },
|
||||
{
|
||||
USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID),
|
||||
.driver_info = (unsigned long)&type_ati
|
||||
},
|
||||
{
|
||||
USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID),
|
||||
.driver_info = (unsigned long)&type_ati
|
||||
},
|
||||
{
|
||||
USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID),
|
||||
.driver_info = (unsigned long)&type_ati
|
||||
},
|
||||
{
|
||||
USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID),
|
||||
.driver_info = (unsigned long)&type_ati
|
||||
},
|
||||
{
|
||||
USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID),
|
||||
.driver_info = (unsigned long)&type_medion
|
||||
},
|
||||
{
|
||||
USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID),
|
||||
.driver_info = (unsigned long)&type_firefly
|
||||
},
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
|
@ -296,25 +321,8 @@ static const struct {
|
|||
{KIND_END, 0x00, EV_MAX + 1, 0, 0}
|
||||
};
|
||||
|
||||
/* Local function prototypes */
|
||||
static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
|
||||
static void ati_remote_irq_out (struct urb *urb);
|
||||
static void ati_remote_irq_in (struct urb *urb);
|
||||
static void ati_remote_input_report (struct urb *urb);
|
||||
static int ati_remote_initialize (struct ati_remote *ati_remote);
|
||||
static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id);
|
||||
static void ati_remote_disconnect (struct usb_interface *interface);
|
||||
|
||||
/* usb specific object to register with the usb subsystem */
|
||||
static struct usb_driver ati_remote_driver = {
|
||||
.name = "ati_remote",
|
||||
.probe = ati_remote_probe,
|
||||
.disconnect = ati_remote_disconnect,
|
||||
.id_table = ati_remote_table,
|
||||
};
|
||||
|
||||
/*
|
||||
* ati_remote_dump_input
|
||||
* ati_remote_dump_input
|
||||
*/
|
||||
static void ati_remote_dump(struct device *dev, unsigned char *data,
|
||||
unsigned int len)
|
||||
|
@ -326,12 +334,14 @@ static void ati_remote_dump(struct device *dev, unsigned char *data,
|
|||
dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
|
||||
data[0], data[1], data[2], data[3]);
|
||||
else
|
||||
dev_warn(dev, "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
|
||||
len, data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
dev_warn(dev,
|
||||
"Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
|
||||
len, data[0], data[1], data[2], data[3], data[4],
|
||||
data[5]);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_open
|
||||
* ati_remote_open
|
||||
*/
|
||||
static int ati_remote_open(struct ati_remote *ati_remote)
|
||||
{
|
||||
|
@ -355,7 +365,7 @@ out: mutex_unlock(&ati_remote->open_mutex);
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_close
|
||||
* ati_remote_close
|
||||
*/
|
||||
static void ati_remote_close(struct ati_remote *ati_remote)
|
||||
{
|
||||
|
@ -390,7 +400,7 @@ static void ati_remote_rc_close(struct rc_dev *rdev)
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_irq_out
|
||||
* ati_remote_irq_out
|
||||
*/
|
||||
static void ati_remote_irq_out(struct urb *urb)
|
||||
{
|
||||
|
@ -408,11 +418,12 @@ static void ati_remote_irq_out(struct urb *urb)
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_sendpacket
|
||||
* ati_remote_sendpacket
|
||||
*
|
||||
* Used to send device initialization strings
|
||||
* Used to send device initialization strings
|
||||
*/
|
||||
static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
|
||||
static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd,
|
||||
unsigned char *data)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
|
@ -441,7 +452,7 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_compute_accel
|
||||
* ati_remote_compute_accel
|
||||
*
|
||||
* Implements acceleration curve for directional control pad
|
||||
* If elapsed time since last event is > 1/4 second, user "stopped",
|
||||
|
@ -478,7 +489,7 @@ static int ati_remote_compute_accel(struct ati_remote *ati_remote)
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_report_input
|
||||
* ati_remote_report_input
|
||||
*/
|
||||
static void ati_remote_input_report(struct urb *urb)
|
||||
{
|
||||
|
@ -518,7 +529,8 @@ static void ati_remote_input_report(struct urb *urb)
|
|||
remote_num = (data[3] >> 4) & 0x0f;
|
||||
if (channel_mask & (1 << (remote_num + 1))) {
|
||||
dbginfo(&ati_remote->interface->dev,
|
||||
"Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
|
||||
"Masked input from channel 0x%02x: data %02x,%02x, "
|
||||
"mask= 0x%02lx\n",
|
||||
remote_num, data[1], data[2], channel_mask);
|
||||
return;
|
||||
}
|
||||
|
@ -546,7 +558,9 @@ static void ati_remote_input_report(struct urb *urb)
|
|||
if (wheel_keycode == KEY_RESERVED) {
|
||||
/* scrollwheel was not mapped, assume mouse */
|
||||
|
||||
/* Look up event code index in the mouse translation table. */
|
||||
/* Look up event code index in the mouse translation
|
||||
* table.
|
||||
*/
|
||||
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
|
||||
if (scancode == ati_remote_tbl[i].data) {
|
||||
index = i;
|
||||
|
@ -630,9 +644,9 @@ static void ati_remote_input_report(struct urb *urb)
|
|||
} else {
|
||||
|
||||
/*
|
||||
* Other event kinds are from the directional control pad, and have an
|
||||
* acceleration factor applied to them. Without this acceleration, the
|
||||
* control pad is mostly unusable.
|
||||
* Other event kinds are from the directional control pad, and
|
||||
* have an acceleration factor applied to them. Without this
|
||||
* acceleration, the control pad is mostly unusable.
|
||||
*/
|
||||
acc = ati_remote_compute_accel(ati_remote);
|
||||
|
||||
|
@ -659,7 +673,8 @@ static void ati_remote_input_report(struct urb *urb)
|
|||
input_report_rel(dev, REL_Y, acc);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
|
||||
dev_dbg(&ati_remote->interface->dev,
|
||||
"ati_remote kind=%d\n",
|
||||
ati_remote_tbl[index].kind);
|
||||
}
|
||||
input_sync(dev);
|
||||
|
@ -670,7 +685,7 @@ static void ati_remote_input_report(struct urb *urb)
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_irq_in
|
||||
* ati_remote_irq_in
|
||||
*/
|
||||
static void ati_remote_irq_in(struct urb *urb)
|
||||
{
|
||||
|
@ -684,22 +699,25 @@ static void ati_remote_irq_in(struct urb *urb)
|
|||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
|
||||
dev_dbg(&ati_remote->interface->dev,
|
||||
"%s: urb error status, unlink?\n",
|
||||
__func__);
|
||||
return;
|
||||
default: /* error */
|
||||
dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
|
||||
dev_dbg(&ati_remote->interface->dev,
|
||||
"%s: Nonzero urb status %d\n",
|
||||
__func__, urb->status);
|
||||
}
|
||||
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
|
||||
dev_err(&ati_remote->interface->dev,
|
||||
"%s: usb_submit_urb()=%d\n",
|
||||
__func__, retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* ati_remote_alloc_buffers
|
||||
* ati_remote_alloc_buffers
|
||||
*/
|
||||
static int ati_remote_alloc_buffers(struct usb_device *udev,
|
||||
struct ati_remote *ati_remote)
|
||||
|
@ -726,7 +744,7 @@ static int ati_remote_alloc_buffers(struct usb_device *udev,
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_free_buffers
|
||||
* ati_remote_free_buffers
|
||||
*/
|
||||
static void ati_remote_free_buffers(struct ati_remote *ati_remote)
|
||||
{
|
||||
|
@ -825,9 +843,10 @@ static int ati_remote_initialize(struct ati_remote *ati_remote)
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_probe
|
||||
* ati_remote_probe
|
||||
*/
|
||||
static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
static int ati_remote_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_host_interface *iface_host = interface->cur_altsetting;
|
||||
|
@ -949,7 +968,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
|
|||
}
|
||||
|
||||
/*
|
||||
* ati_remote_disconnect
|
||||
* ati_remote_disconnect
|
||||
*/
|
||||
static void ati_remote_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
|
@ -971,6 +990,14 @@ static void ati_remote_disconnect(struct usb_interface *interface)
|
|||
kfree(ati_remote);
|
||||
}
|
||||
|
||||
/* usb specific object to register with the usb subsystem */
|
||||
static struct usb_driver ati_remote_driver = {
|
||||
.name = "ati_remote",
|
||||
.probe = ati_remote_probe,
|
||||
.disconnect = ati_remote_disconnect,
|
||||
.id_table = ati_remote_table,
|
||||
};
|
||||
|
||||
module_usb_driver(ati_remote_driver);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
|
|
|
@ -0,0 +1,639 @@
|
|||
/*
|
||||
* IguanaWorks USB IR Transceiver support
|
||||
*
|
||||
* Copyright (C) 2012 Sean Young <sean@mess.org>
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/completion.h>
|
||||
#include <media/rc-core.h>
|
||||
|
||||
#define DRIVER_NAME "iguanair"
|
||||
|
||||
struct iguanair {
|
||||
struct rc_dev *rc;
|
||||
|
||||
struct device *dev;
|
||||
struct usb_device *udev;
|
||||
|
||||
int pipe_in, pipe_out;
|
||||
uint8_t bufsize;
|
||||
uint8_t version[2];
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
/* receiver support */
|
||||
bool receiver_on;
|
||||
dma_addr_t dma_in;
|
||||
uint8_t *buf_in;
|
||||
struct urb *urb_in;
|
||||
struct completion completion;
|
||||
|
||||
/* transmit support */
|
||||
bool tx_overflow;
|
||||
uint32_t carrier;
|
||||
uint8_t cycle_overhead;
|
||||
uint8_t channels;
|
||||
uint8_t busy4;
|
||||
uint8_t busy7;
|
||||
|
||||
char name[64];
|
||||
char phys[64];
|
||||
};
|
||||
|
||||
#define CMD_GET_VERSION 0x01
|
||||
#define CMD_GET_BUFSIZE 0x11
|
||||
#define CMD_GET_FEATURES 0x10
|
||||
#define CMD_SEND 0x15
|
||||
#define CMD_EXECUTE 0x1f
|
||||
#define CMD_RX_OVERFLOW 0x31
|
||||
#define CMD_TX_OVERFLOW 0x32
|
||||
#define CMD_RECEIVER_ON 0x12
|
||||
#define CMD_RECEIVER_OFF 0x14
|
||||
|
||||
#define DIR_IN 0xdc
|
||||
#define DIR_OUT 0xcd
|
||||
|
||||
#define MAX_PACKET_SIZE 8u
|
||||
#define TIMEOUT 1000
|
||||
|
||||
struct packet {
|
||||
uint16_t start;
|
||||
uint8_t direction;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
struct response_packet {
|
||||
struct packet header;
|
||||
uint8_t data[4];
|
||||
};
|
||||
|
||||
struct send_packet {
|
||||
struct packet header;
|
||||
uint8_t length;
|
||||
uint8_t channels;
|
||||
uint8_t busy7;
|
||||
uint8_t busy4;
|
||||
uint8_t payload[0];
|
||||
};
|
||||
|
||||
static void process_ir_data(struct iguanair *ir, unsigned len)
|
||||
{
|
||||
if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
|
||||
switch (ir->buf_in[3]) {
|
||||
case CMD_TX_OVERFLOW:
|
||||
ir->tx_overflow = true;
|
||||
case CMD_RECEIVER_OFF:
|
||||
case CMD_RECEIVER_ON:
|
||||
case CMD_SEND:
|
||||
complete(&ir->completion);
|
||||
break;
|
||||
case CMD_RX_OVERFLOW:
|
||||
dev_warn(ir->dev, "receive overflow\n");
|
||||
break;
|
||||
default:
|
||||
dev_warn(ir->dev, "control code %02x received\n",
|
||||
ir->buf_in[3]);
|
||||
break;
|
||||
}
|
||||
} else if (len >= 7) {
|
||||
DEFINE_IR_RAW_EVENT(rawir);
|
||||
unsigned i;
|
||||
|
||||
init_ir_raw_event(&rawir);
|
||||
|
||||
for (i = 0; i < 7; i++) {
|
||||
if (ir->buf_in[i] == 0x80) {
|
||||
rawir.pulse = false;
|
||||
rawir.duration = US_TO_NS(21845);
|
||||
} else {
|
||||
rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
|
||||
rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
|
||||
21330;
|
||||
}
|
||||
|
||||
ir_raw_event_store_with_filter(ir->rc, &rawir);
|
||||
}
|
||||
|
||||
ir_raw_event_handle(ir->rc);
|
||||
}
|
||||
}
|
||||
|
||||
static void iguanair_rx(struct urb *urb)
|
||||
{
|
||||
struct iguanair *ir;
|
||||
|
||||
if (!urb)
|
||||
return;
|
||||
|
||||
ir = urb->context;
|
||||
if (!ir) {
|
||||
usb_unlink_urb(urb);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
process_ir_data(ir, urb->actual_length);
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
usb_unlink_urb(urb);
|
||||
return;
|
||||
case -EPIPE:
|
||||
default:
|
||||
dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
|
||||
break;
|
||||
}
|
||||
|
||||
usb_submit_urb(urb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
|
||||
struct response_packet *response, unsigned *res_len)
|
||||
{
|
||||
unsigned offset, len;
|
||||
int rc, transferred;
|
||||
|
||||
for (offset = 0; offset < size; offset += MAX_PACKET_SIZE) {
|
||||
len = min(size - offset, MAX_PACKET_SIZE);
|
||||
|
||||
if (ir->tx_overflow)
|
||||
return -EOVERFLOW;
|
||||
|
||||
rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data + offset,
|
||||
len, &transferred, TIMEOUT);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (transferred != len)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (response) {
|
||||
rc = usb_interrupt_msg(ir->udev, ir->pipe_in, response,
|
||||
sizeof(*response), res_len, TIMEOUT);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int iguanair_get_features(struct iguanair *ir)
|
||||
{
|
||||
struct packet packet;
|
||||
struct response_packet response;
|
||||
int rc, len;
|
||||
|
||||
packet.start = 0;
|
||||
packet.direction = DIR_OUT;
|
||||
packet.cmd = CMD_GET_VERSION;
|
||||
|
||||
rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
|
||||
if (rc) {
|
||||
dev_info(ir->dev, "failed to get version\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len != 6) {
|
||||
dev_info(ir->dev, "failed to get version\n");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ir->version[0] = response.data[0];
|
||||
ir->version[1] = response.data[1];
|
||||
ir->bufsize = 150;
|
||||
ir->cycle_overhead = 65;
|
||||
|
||||
packet.cmd = CMD_GET_BUFSIZE;
|
||||
|
||||
rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
|
||||
if (rc) {
|
||||
dev_info(ir->dev, "failed to get buffer size\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len != 5) {
|
||||
dev_info(ir->dev, "failed to get buffer size\n");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ir->bufsize = response.data[0];
|
||||
|
||||
if (ir->version[0] == 0 || ir->version[1] == 0)
|
||||
goto out;
|
||||
|
||||
packet.cmd = CMD_GET_FEATURES;
|
||||
|
||||
rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
|
||||
if (rc) {
|
||||
dev_info(ir->dev, "failed to get features\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len < 5) {
|
||||
dev_info(ir->dev, "failed to get features\n");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len > 5 && ir->version[0] >= 4)
|
||||
ir->cycle_overhead = response.data[1];
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int iguanair_receiver(struct iguanair *ir, bool enable)
|
||||
{
|
||||
struct packet packet = { 0, DIR_OUT, enable ?
|
||||
CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
|
||||
int rc;
|
||||
|
||||
INIT_COMPLETION(ir->completion);
|
||||
|
||||
rc = iguanair_send(ir, &packet, sizeof(packet), NULL, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
wait_for_completion_timeout(&ir->completion, TIMEOUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The iguana ir creates the carrier by busy spinning after each pulse or
|
||||
* space. This is counted in CPU cycles, with the CPU running at 24MHz. It is
|
||||
* broken down into 7-cycles and 4-cyles delays, with a preference for
|
||||
* 4-cycle delays.
|
||||
*/
|
||||
static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
|
||||
{
|
||||
struct iguanair *ir = dev->priv;
|
||||
|
||||
if (carrier < 25000 || carrier > 150000)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ir->lock);
|
||||
|
||||
if (carrier != ir->carrier) {
|
||||
uint32_t cycles, fours, sevens;
|
||||
|
||||
ir->carrier = carrier;
|
||||
|
||||
cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
|
||||
ir->cycle_overhead;
|
||||
|
||||
/* make up the the remainer of 4-cycle blocks */
|
||||
switch (cycles & 3) {
|
||||
case 0:
|
||||
sevens = 0;
|
||||
break;
|
||||
case 1:
|
||||
sevens = 3;
|
||||
break;
|
||||
case 2:
|
||||
sevens = 2;
|
||||
break;
|
||||
case 3:
|
||||
sevens = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
fours = (cycles - sevens * 7) / 4;
|
||||
|
||||
/* magic happens here */
|
||||
ir->busy7 = (4 - sevens) * 2;
|
||||
ir->busy4 = 110 - fours;
|
||||
}
|
||||
|
||||
mutex_unlock(&ir->lock);
|
||||
|
||||
return carrier;
|
||||
}
|
||||
|
||||
static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
|
||||
{
|
||||
struct iguanair *ir = dev->priv;
|
||||
|
||||
if (mask > 15)
|
||||
return 4;
|
||||
|
||||
mutex_lock(&ir->lock);
|
||||
ir->channels = mask;
|
||||
mutex_unlock(&ir->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
|
||||
{
|
||||
struct iguanair *ir = dev->priv;
|
||||
uint8_t space, *payload;
|
||||
unsigned i, size, rc;
|
||||
struct send_packet *packet;
|
||||
|
||||
mutex_lock(&ir->lock);
|
||||
|
||||
/* convert from us to carrier periods */
|
||||
for (i = size = 0; i < count; i++) {
|
||||
txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
|
||||
size += (txbuf[i] + 126) / 127;
|
||||
}
|
||||
|
||||
packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
|
||||
if (!packet) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (size > ir->bufsize) {
|
||||
rc = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
packet->header.start = 0;
|
||||
packet->header.direction = DIR_OUT;
|
||||
packet->header.cmd = CMD_SEND;
|
||||
packet->length = size;
|
||||
packet->channels = ir->channels << 4;
|
||||
packet->busy7 = ir->busy7;
|
||||
packet->busy4 = ir->busy4;
|
||||
|
||||
space = 0;
|
||||
payload = packet->payload;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
unsigned periods = txbuf[i];
|
||||
|
||||
while (periods > 127) {
|
||||
*payload++ = 127 | space;
|
||||
periods -= 127;
|
||||
}
|
||||
|
||||
*payload++ = periods | space;
|
||||
space ^= 0x80;
|
||||
}
|
||||
|
||||
if (ir->receiver_on) {
|
||||
rc = iguanair_receiver(ir, false);
|
||||
if (rc) {
|
||||
dev_warn(ir->dev, "disable receiver before transmit failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ir->tx_overflow = false;
|
||||
|
||||
INIT_COMPLETION(ir->completion);
|
||||
|
||||
rc = iguanair_send(ir, packet, size + 8, NULL, NULL);
|
||||
|
||||
if (rc == 0) {
|
||||
wait_for_completion_timeout(&ir->completion, TIMEOUT);
|
||||
if (ir->tx_overflow)
|
||||
rc = -EOVERFLOW;
|
||||
}
|
||||
|
||||
ir->tx_overflow = false;
|
||||
|
||||
if (ir->receiver_on) {
|
||||
if (iguanair_receiver(ir, true))
|
||||
dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&ir->lock);
|
||||
kfree(packet);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int iguanair_open(struct rc_dev *rdev)
|
||||
{
|
||||
struct iguanair *ir = rdev->priv;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&ir->lock);
|
||||
|
||||
usb_submit_urb(ir->urb_in, GFP_KERNEL);
|
||||
|
||||
BUG_ON(ir->receiver_on);
|
||||
|
||||
rc = iguanair_receiver(ir, true);
|
||||
if (rc == 0)
|
||||
ir->receiver_on = true;
|
||||
|
||||
mutex_unlock(&ir->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void iguanair_close(struct rc_dev *rdev)
|
||||
{
|
||||
struct iguanair *ir = rdev->priv;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&ir->lock);
|
||||
|
||||
rc = iguanair_receiver(ir, false);
|
||||
ir->receiver_on = false;
|
||||
if (rc)
|
||||
dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
|
||||
|
||||
usb_kill_urb(ir->urb_in);
|
||||
|
||||
mutex_unlock(&ir->lock);
|
||||
}
|
||||
|
||||
static int __devinit iguanair_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct iguanair *ir;
|
||||
struct rc_dev *rc;
|
||||
int ret;
|
||||
struct usb_host_interface *idesc;
|
||||
|
||||
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
|
||||
rc = rc_allocate_device();
|
||||
if (!ir || !rc) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_ATOMIC,
|
||||
&ir->dma_in);
|
||||
ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
|
||||
|
||||
if (!ir->buf_in || !ir->urb_in) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
idesc = intf->altsetting;
|
||||
|
||||
if (idesc->desc.bNumEndpoints < 2) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ir->rc = rc;
|
||||
ir->dev = &intf->dev;
|
||||
ir->udev = udev;
|
||||
ir->pipe_in = usb_rcvintpipe(udev,
|
||||
idesc->endpoint[0].desc.bEndpointAddress);
|
||||
ir->pipe_out = usb_sndintpipe(udev,
|
||||
idesc->endpoint[1].desc.bEndpointAddress);
|
||||
mutex_init(&ir->lock);
|
||||
init_completion(&ir->completion);
|
||||
|
||||
ret = iguanair_get_features(ir);
|
||||
if (ret) {
|
||||
dev_warn(&intf->dev, "failed to get device features");
|
||||
goto out;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(ir->urb_in, ir->udev, ir->pipe_in, ir->buf_in,
|
||||
MAX_PACKET_SIZE, iguanair_rx, ir,
|
||||
idesc->endpoint[0].desc.bInterval);
|
||||
ir->urb_in->transfer_dma = ir->dma_in;
|
||||
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
snprintf(ir->name, sizeof(ir->name),
|
||||
"IguanaWorks USB IR Transceiver version %d.%d",
|
||||
ir->version[0], ir->version[1]);
|
||||
|
||||
usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
|
||||
|
||||
rc->input_name = ir->name;
|
||||
rc->input_phys = ir->phys;
|
||||
usb_to_input_id(ir->udev, &rc->input_id);
|
||||
rc->dev.parent = &intf->dev;
|
||||
rc->driver_type = RC_DRIVER_IR_RAW;
|
||||
rc->allowed_protos = RC_TYPE_ALL;
|
||||
rc->priv = ir;
|
||||
rc->open = iguanair_open;
|
||||
rc->close = iguanair_close;
|
||||
rc->s_tx_mask = iguanair_set_tx_mask;
|
||||
rc->s_tx_carrier = iguanair_set_tx_carrier;
|
||||
rc->tx_ir = iguanair_tx;
|
||||
rc->driver_name = DRIVER_NAME;
|
||||
rc->map_name = RC_MAP_EMPTY;
|
||||
|
||||
iguanair_set_tx_carrier(rc, 38000);
|
||||
|
||||
ret = rc_register_device(rc);
|
||||
if (ret < 0) {
|
||||
dev_err(&intf->dev, "failed to register rc device %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
usb_set_intfdata(intf, ir);
|
||||
|
||||
dev_info(&intf->dev, "Registered %s", ir->name);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
if (ir) {
|
||||
usb_free_urb(ir->urb_in);
|
||||
usb_free_coherent(udev, MAX_PACKET_SIZE, ir->buf_in,
|
||||
ir->dma_in);
|
||||
}
|
||||
rc_free_device(rc);
|
||||
kfree(ir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit iguanair_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct iguanair *ir = usb_get_intfdata(intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
usb_kill_urb(ir->urb_in);
|
||||
usb_free_urb(ir->urb_in);
|
||||
usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in);
|
||||
rc_unregister_device(ir->rc);
|
||||
kfree(ir);
|
||||
}
|
||||
|
||||
static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct iguanair *ir = usb_get_intfdata(intf);
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&ir->lock);
|
||||
|
||||
if (ir->receiver_on) {
|
||||
rc = iguanair_receiver(ir, false);
|
||||
if (rc)
|
||||
dev_warn(ir->dev, "failed to disable receiver for suspend\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&ir->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int iguanair_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct iguanair *ir = usb_get_intfdata(intf);
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&ir->lock);
|
||||
|
||||
if (ir->receiver_on) {
|
||||
rc = iguanair_receiver(ir, true);
|
||||
if (rc)
|
||||
dev_warn(ir->dev, "failed to enable receiver after resume\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&ir->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct usb_device_id iguanair_table[] = {
|
||||
{ USB_DEVICE(0x1781, 0x0938) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct usb_driver iguanair_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.probe = iguanair_probe,
|
||||
.disconnect = __devexit_p(iguanair_disconnect),
|
||||
.suspend = iguanair_suspend,
|
||||
.resume = iguanair_resume,
|
||||
.reset_resume = iguanair_resume,
|
||||
.id_table = iguanair_table
|
||||
};
|
||||
|
||||
module_usb_driver(iguanair_driver);
|
||||
|
||||
MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
|
||||
MODULE_AUTHOR("Sean Young <sean@mess.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DEVICE_TABLE(usb, iguanair_table);
|
||||
|
|
@ -199,6 +199,7 @@ static bool debug;
|
|||
#define VENDOR_REALTEK 0x0bda
|
||||
#define VENDOR_TIVO 0x105a
|
||||
#define VENDOR_CONEXANT 0x0572
|
||||
#define VENDOR_TWISTEDMELON 0x2596
|
||||
|
||||
enum mceusb_model_type {
|
||||
MCE_GEN2 = 0, /* Most boards */
|
||||
|
@ -391,6 +392,12 @@ static struct usb_device_id mceusb_dev_table[] = {
|
|||
/* Conexant Hybrid TV RDU253S Polaris */
|
||||
{ USB_DEVICE(VENDOR_CONEXANT, 0x58a5),
|
||||
.driver_info = CX_HYBRID_TV },
|
||||
/* Twisted Melon Inc. - Manta Mini Receiver */
|
||||
{ USB_DEVICE(VENDOR_TWISTEDMELON, 0x8008) },
|
||||
/* Twisted Melon Inc. - Manta Pico Receiver */
|
||||
{ USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) },
|
||||
/* Twisted Melon Inc. - Manta Transceiver */
|
||||
{ USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) },
|
||||
/* Terminating entry */
|
||||
{ }
|
||||
};
|
||||
|
@ -410,14 +417,12 @@ struct mceusb_dev {
|
|||
/* usb */
|
||||
struct usb_device *usbdev;
|
||||
struct urb *urb_in;
|
||||
struct usb_endpoint_descriptor *usb_ep_in;
|
||||
struct usb_endpoint_descriptor *usb_ep_out;
|
||||
|
||||
/* buffers and dma */
|
||||
unsigned char *buf_in;
|
||||
unsigned int len_in;
|
||||
dma_addr_t dma_in;
|
||||
dma_addr_t dma_out;
|
||||
|
||||
enum {
|
||||
CMD_HEADER = 0,
|
||||
|
@ -686,7 +691,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
|
|||
dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem);
|
||||
}
|
||||
|
||||
static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
|
||||
static void mce_async_callback(struct urb *urb)
|
||||
{
|
||||
struct mceusb_dev *ir;
|
||||
int len;
|
||||
|
@ -733,7 +738,7 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
|
|||
pipe = usb_sndintpipe(ir->usbdev,
|
||||
ir->usb_ep_out->bEndpointAddress);
|
||||
usb_fill_int_urb(async_urb, ir->usbdev, pipe,
|
||||
async_buf, size, (usb_complete_t)mce_async_callback,
|
||||
async_buf, size, mce_async_callback,
|
||||
ir, ir->usb_ep_out->bInterval);
|
||||
memcpy(async_buf, data, size);
|
||||
|
||||
|
@ -1031,7 +1036,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
|
|||
ir_raw_event_handle(ir->rc);
|
||||
}
|
||||
|
||||
static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
|
||||
static void mceusb_dev_recv(struct urb *urb)
|
||||
{
|
||||
struct mceusb_dev *ir;
|
||||
int buf_len;
|
||||
|
@ -1331,7 +1336,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
|
|||
ir->model = model;
|
||||
|
||||
/* Saving usb interface data for use by the transmitter routine */
|
||||
ir->usb_ep_in = ep_in;
|
||||
ir->usb_ep_out = ep_out;
|
||||
|
||||
if (dev->descriptor.iManufacturer
|
||||
|
@ -1349,8 +1353,8 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
|
|||
goto rc_dev_fail;
|
||||
|
||||
/* wire up inbound data handler */
|
||||
usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
|
||||
maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
|
||||
usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
|
||||
mceusb_dev_recv, ir, ep_in->bInterval);
|
||||
ir->urb_in->transfer_dma = ir->dma_in;
|
||||
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
|
|
|
@ -775,10 +775,11 @@ static ssize_t show_protocols(struct device *device,
|
|||
if (dev->driver_type == RC_DRIVER_SCANCODE) {
|
||||
enabled = dev->rc_map.rc_type;
|
||||
allowed = dev->allowed_protos;
|
||||
} else {
|
||||
} else if (dev->raw) {
|
||||
enabled = dev->raw->enabled_protocols;
|
||||
allowed = ir_raw_get_allowed_protocols();
|
||||
}
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n",
|
||||
(long long)allowed,
|
||||
|
|
|
@ -26,11 +26,10 @@
|
|||
#include <media/v4l2-ioctl.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define DRIVER_NAME "adv7180"
|
||||
|
||||
#define ADV7180_INPUT_CONTROL_REG 0x00
|
||||
#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
|
||||
#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
|
||||
|
@ -55,21 +54,21 @@
|
|||
|
||||
#define ADV7180_AUTODETECT_ENABLE_REG 0x07
|
||||
#define ADV7180_AUTODETECT_DEFAULT 0x7f
|
||||
|
||||
/* Contrast */
|
||||
#define ADV7180_CON_REG 0x08 /*Unsigned */
|
||||
#define CON_REG_MIN 0
|
||||
#define CON_REG_DEF 128
|
||||
#define CON_REG_MAX 255
|
||||
|
||||
#define ADV7180_CON_MIN 0
|
||||
#define ADV7180_CON_DEF 128
|
||||
#define ADV7180_CON_MAX 255
|
||||
/* Brightness*/
|
||||
#define ADV7180_BRI_REG 0x0a /*Signed */
|
||||
#define BRI_REG_MIN -128
|
||||
#define BRI_REG_DEF 0
|
||||
#define BRI_REG_MAX 127
|
||||
|
||||
#define ADV7180_BRI_MIN -128
|
||||
#define ADV7180_BRI_DEF 0
|
||||
#define ADV7180_BRI_MAX 127
|
||||
/* Hue */
|
||||
#define ADV7180_HUE_REG 0x0b /*Signed, inverted */
|
||||
#define HUE_REG_MIN -127
|
||||
#define HUE_REG_DEF 0
|
||||
#define HUE_REG_MAX 128
|
||||
#define ADV7180_HUE_MIN -127
|
||||
#define ADV7180_HUE_DEF 0
|
||||
#define ADV7180_HUE_MAX 128
|
||||
|
||||
#define ADV7180_ADI_CTRL_REG 0x0e
|
||||
#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20
|
||||
|
@ -98,12 +97,12 @@
|
|||
#define ADV7180_ICONF1_ACTIVE_LOW 0x01
|
||||
#define ADV7180_ICONF1_PSYNC_ONLY 0x10
|
||||
#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
|
||||
|
||||
/* Saturation */
|
||||
#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */
|
||||
#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */
|
||||
#define SAT_REG_MIN 0
|
||||
#define SAT_REG_DEF 128
|
||||
#define SAT_REG_MAX 255
|
||||
#define ADV7180_SAT_MIN 0
|
||||
#define ADV7180_SAT_DEF 128
|
||||
#define ADV7180_SAT_MAX 255
|
||||
|
||||
#define ADV7180_IRQ1_LOCK 0x01
|
||||
#define ADV7180_IRQ1_UNLOCK 0x02
|
||||
|
@ -121,18 +120,18 @@
|
|||
#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F
|
||||
|
||||
struct adv7180_state {
|
||||
struct v4l2_ctrl_handler ctrl_hdl;
|
||||
struct v4l2_subdev sd;
|
||||
struct work_struct work;
|
||||
struct mutex mutex; /* mutual excl. when accessing chip */
|
||||
int irq;
|
||||
v4l2_std_id curr_norm;
|
||||
bool autodetect;
|
||||
s8 brightness;
|
||||
s16 hue;
|
||||
u8 contrast;
|
||||
u8 saturation;
|
||||
u8 input;
|
||||
};
|
||||
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
|
||||
struct adv7180_state, \
|
||||
ctrl_hdl)->sd)
|
||||
|
||||
static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
|
||||
{
|
||||
|
@ -237,7 +236,7 @@ static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*We cannot discriminate between LQFP and 40-pin LFCSP, so accept
|
||||
/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
|
||||
* all inputs and let the card driver take care of validation
|
||||
*/
|
||||
if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
|
||||
|
@ -316,117 +315,39 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
|
||||
{
|
||||
switch (qc->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX,
|
||||
1, BRI_REG_DEF);
|
||||
case V4L2_CID_HUE:
|
||||
return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX,
|
||||
1, HUE_REG_DEF);
|
||||
case V4L2_CID_CONTRAST:
|
||||
return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX,
|
||||
1, CON_REG_DEF);
|
||||
case V4L2_CID_SATURATION:
|
||||
return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX,
|
||||
1, SAT_REG_DEF);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
struct adv7180_state *state = to_state(sd);
|
||||
int ret = mutex_lock_interruptible(&state->mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
ctrl->value = state->brightness;
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
ctrl->value = state->hue;
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
ctrl->value = state->contrast;
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
ctrl->value = state->saturation;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&state->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
|
||||
struct adv7180_state *state = to_state(sd);
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
int ret = mutex_lock_interruptible(&state->mutex);
|
||||
int val;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = ctrl->val;
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
if ((ctrl->value > BRI_REG_MAX)
|
||||
|| (ctrl->value < BRI_REG_MIN)) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
state->brightness = ctrl->value;
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
ADV7180_BRI_REG,
|
||||
state->brightness);
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val);
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
if ((ctrl->value > HUE_REG_MAX)
|
||||
|| (ctrl->value < HUE_REG_MIN)) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
state->hue = ctrl->value;
|
||||
/*Hue is inverted according to HSL chart */
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
ADV7180_HUE_REG, -state->hue);
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
if ((ctrl->value > CON_REG_MAX)
|
||||
|| (ctrl->value < CON_REG_MIN)) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
state->contrast = ctrl->value;
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
ADV7180_CON_REG,
|
||||
state->contrast);
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
if ((ctrl->value > SAT_REG_MAX)
|
||||
|| (ctrl->value < SAT_REG_MIN)) {
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
*This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
|
||||
*Let's not confuse the user, everybody understands saturation
|
||||
*/
|
||||
state->saturation = ctrl->value;
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
ADV7180_SD_SAT_CB_REG,
|
||||
state->saturation);
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
|
||||
val);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
ADV7180_SD_SAT_CR_REG,
|
||||
state->saturation);
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
|
||||
val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -436,6 +357,42 @@ static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
|
||||
.s_ctrl = adv7180_s_ctrl,
|
||||
};
|
||||
|
||||
static int adv7180_init_controls(struct adv7180_state *state)
|
||||
{
|
||||
v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
|
||||
|
||||
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
|
||||
ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
|
||||
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, ADV7180_CON_MIN,
|
||||
ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
|
||||
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
|
||||
V4L2_CID_SATURATION, ADV7180_SAT_MIN,
|
||||
ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
|
||||
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
|
||||
V4L2_CID_HUE, ADV7180_HUE_MIN,
|
||||
ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
|
||||
state->sd.ctrl_handler = &state->ctrl_hdl;
|
||||
if (state->ctrl_hdl.error) {
|
||||
int err = state->ctrl_hdl.error;
|
||||
|
||||
v4l2_ctrl_handler_free(&state->ctrl_hdl);
|
||||
return err;
|
||||
}
|
||||
v4l2_ctrl_handler_setup(&state->ctrl_hdl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static void adv7180_exit_controls(struct adv7180_state *state)
|
||||
{
|
||||
v4l2_ctrl_handler_free(&state->ctrl_hdl);
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
|
||||
.querystd = adv7180_querystd,
|
||||
.g_input_status = adv7180_g_input_status,
|
||||
|
@ -445,9 +402,9 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
|
|||
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
|
||||
.g_chip_ident = adv7180_g_chip_ident,
|
||||
.s_std = adv7180_s_std,
|
||||
.queryctrl = adv7180_queryctrl,
|
||||
.g_ctrl = adv7180_g_ctrl,
|
||||
.s_ctrl = adv7180_s_ctrl,
|
||||
.queryctrl = v4l2_subdev_queryctrl,
|
||||
.g_ctrl = v4l2_subdev_g_ctrl,
|
||||
.s_ctrl = v4l2_subdev_s_ctrl,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops adv7180_ops = {
|
||||
|
@ -539,7 +496,7 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
|
|||
|
||||
/* register for interrupts */
|
||||
if (state->irq > 0) {
|
||||
ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
|
||||
ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME,
|
||||
state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -580,31 +537,6 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*Set default value for controls */
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG,
|
||||
state->brightness);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG,
|
||||
state->contrast);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
|
||||
state->saturation);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
|
||||
state->saturation);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -632,25 +564,26 @@ static __devinit int adv7180_probe(struct i2c_client *client,
|
|||
INIT_WORK(&state->work, adv7180_work);
|
||||
mutex_init(&state->mutex);
|
||||
state->autodetect = true;
|
||||
state->brightness = BRI_REG_DEF;
|
||||
state->hue = HUE_REG_DEF;
|
||||
state->contrast = CON_REG_DEF;
|
||||
state->saturation = SAT_REG_DEF;
|
||||
state->input = 0;
|
||||
sd = &state->sd;
|
||||
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
|
||||
|
||||
ret = init_device(client, state);
|
||||
if (0 != ret)
|
||||
ret = adv7180_init_controls(state);
|
||||
if (ret)
|
||||
goto err_unreg_subdev;
|
||||
ret = init_device(client, state);
|
||||
if (ret)
|
||||
goto err_free_ctrl;
|
||||
return 0;
|
||||
|
||||
err_free_ctrl:
|
||||
adv7180_exit_controls(state);
|
||||
err_unreg_subdev:
|
||||
mutex_destroy(&state->mutex);
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
kfree(state);
|
||||
err:
|
||||
printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret);
|
||||
printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -678,7 +611,7 @@ static __devexit int adv7180_remove(struct i2c_client *client)
|
|||
}
|
||||
|
||||
static const struct i2c_device_id adv7180_id[] = {
|
||||
{DRIVER_NAME, 0},
|
||||
{KBUILD_MODNAME, 0},
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -716,7 +649,7 @@ MODULE_DEVICE_TABLE(i2c, adv7180_id);
|
|||
static struct i2c_driver adv7180_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRIVER_NAME,
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = adv7180_probe,
|
||||
.remove = __devexit_p(adv7180_remove),
|
||||
|
|
|
@ -345,7 +345,7 @@ static struct CARD {
|
|||
{ 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" },
|
||||
{ 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" },
|
||||
{ 0x3116f200, BTTV_BOARD_TVT_TD3116, "Tongwei Video Technology TD-3116" },
|
||||
|
||||
{ 0x02280279, BTTV_BOARD_APOSONIC_WDVR, "Aposonic W-DVR" },
|
||||
{ 0, -1, NULL }
|
||||
};
|
||||
|
||||
|
@ -2818,6 +2818,14 @@ struct tvcard bttv_tvcards[] = {
|
|||
.pll = PLL_28,
|
||||
.tuner_type = TUNER_ABSENT,
|
||||
},
|
||||
[BTTV_BOARD_APOSONIC_WDVR] = {
|
||||
.name = "Aposonic W-DVR",
|
||||
.video_inputs = 4,
|
||||
.svhs = NO_SVHS,
|
||||
.muxsel = MUXSEL(2, 3, 1, 0),
|
||||
.tuner_type = TUNER_ABSENT,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
|
||||
|
|
|
@ -184,7 +184,7 @@
|
|||
#define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e
|
||||
#define BTTV_BOARD_PV183 0x9f
|
||||
#define BTTV_BOARD_TVT_TD3116 0xa0
|
||||
|
||||
#define BTTV_BOARD_APOSONIC_WDVR 0xa1
|
||||
|
||||
/* more card-specific defines */
|
||||
#define PT2254_L_CHANNEL 0x10
|
||||
|
|
|
@ -499,16 +499,12 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus)
|
|||
|
||||
BUG_ON(!dev->cx231xx_send_usb_command);
|
||||
|
||||
memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
|
||||
memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
|
||||
memcpy(&bus->i2c_client, &cx231xx_client_template,
|
||||
sizeof(bus->i2c_client));
|
||||
|
||||
bus->i2c_adap = cx231xx_adap_template;
|
||||
bus->i2c_client = cx231xx_client_template;
|
||||
bus->i2c_adap.dev.parent = &dev->udev->dev;
|
||||
|
||||
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
|
||||
|
||||
bus->i2c_algo.data = bus;
|
||||
bus->i2c_adap.algo_data = bus;
|
||||
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
|
||||
i2c_add_adapter(&bus->i2c_adap);
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
|
@ -481,7 +480,6 @@ struct cx231xx_i2c {
|
|||
|
||||
/* i2c i/o */
|
||||
struct i2c_adapter i2c_adap;
|
||||
struct i2c_algo_bit_data i2c_algo;
|
||||
struct i2c_client i2c_client;
|
||||
u32 i2c_rc;
|
||||
|
||||
|
|
|
@ -316,19 +316,13 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
|
|||
|
||||
dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
|
||||
|
||||
memcpy(&bus->i2c_adap, &cx23885_i2c_adap_template,
|
||||
sizeof(bus->i2c_adap));
|
||||
memcpy(&bus->i2c_algo, &cx23885_i2c_algo_template,
|
||||
sizeof(bus->i2c_algo));
|
||||
memcpy(&bus->i2c_client, &cx23885_i2c_client_template,
|
||||
sizeof(bus->i2c_client));
|
||||
|
||||
bus->i2c_adap = cx23885_i2c_adap_template;
|
||||
bus->i2c_client = cx23885_i2c_client_template;
|
||||
bus->i2c_adap.dev.parent = &dev->pci->dev;
|
||||
|
||||
strlcpy(bus->i2c_adap.name, bus->dev->name,
|
||||
sizeof(bus->i2c_adap.name));
|
||||
|
||||
bus->i2c_algo.data = bus;
|
||||
bus->i2c_adap.algo_data = bus;
|
||||
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
|
||||
i2c_add_adapter(&bus->i2c_adap);
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -247,7 +246,6 @@ struct cx23885_i2c {
|
|||
|
||||
/* i2c i/o */
|
||||
struct i2c_adapter i2c_adap;
|
||||
struct i2c_algo_bit_data i2c_algo;
|
||||
struct i2c_client i2c_client;
|
||||
u32 i2c_rc;
|
||||
|
||||
|
|
|
@ -305,18 +305,12 @@ int cx25821_i2c_register(struct cx25821_i2c *bus)
|
|||
|
||||
dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
|
||||
|
||||
memcpy(&bus->i2c_adap, &cx25821_i2c_adap_template,
|
||||
sizeof(bus->i2c_adap));
|
||||
memcpy(&bus->i2c_algo, &cx25821_i2c_algo_template,
|
||||
sizeof(bus->i2c_algo));
|
||||
memcpy(&bus->i2c_client, &cx25821_i2c_client_template,
|
||||
sizeof(bus->i2c_client));
|
||||
|
||||
bus->i2c_adap = cx25821_i2c_adap_template;
|
||||
bus->i2c_client = cx25821_i2c_client_template;
|
||||
bus->i2c_adap.dev.parent = &dev->pci->dev;
|
||||
|
||||
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
|
||||
|
||||
bus->i2c_algo.data = bus;
|
||||
bus->i2c_adap.algo_data = bus;
|
||||
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
|
||||
i2c_add_adapter(&bus->i2c_adap);
|
||||
|
|
|
@ -499,7 +499,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
|
|||
mutex_lock(&dev->lock);
|
||||
|
||||
/* no support */
|
||||
if (decoder < VDEC_A && decoder > VDEC_H) {
|
||||
if (decoder < VDEC_A || decoder > VDEC_H) {
|
||||
mutex_unlock(&dev->lock);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -213,7 +212,6 @@ struct cx25821_i2c {
|
|||
|
||||
/* i2c i/o */
|
||||
struct i2c_adapter i2c_adap;
|
||||
struct i2c_algo_bit_data i2c_algo;
|
||||
struct i2c_client i2c_client;
|
||||
u32 i2c_rc;
|
||||
|
||||
|
|
|
@ -1,30 +1,34 @@
|
|||
config DISPLAY_DAVINCI_DM646X_EVM
|
||||
tristate "DM646x EVM Video Display"
|
||||
depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
|
||||
select VIDEOBUF_DMA_CONTIG
|
||||
config VIDEO_DAVINCI_VPIF_DISPLAY
|
||||
tristate "DM646x/DA850/OMAPL138 EVM Video Display"
|
||||
depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select VIDEO_DAVINCI_VPIF
|
||||
select VIDEO_ADV7343
|
||||
select VIDEO_THS7303
|
||||
select VIDEO_ADV7343 if VIDEO_HELPER_CHIPS_AUTO
|
||||
select VIDEO_THS7303 if VIDEO_HELPER_CHIPS_AUTO
|
||||
help
|
||||
Support for DM6467 based display device.
|
||||
Enables Davinci VPIF module used for display devices.
|
||||
This module is common for following DM6467/DA850/OMAPL138
|
||||
based display devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vpif_display.
|
||||
|
||||
config CAPTURE_DAVINCI_DM646X_EVM
|
||||
tristate "DM646x EVM Video Capture"
|
||||
depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
|
||||
select VIDEOBUF_DMA_CONTIG
|
||||
config VIDEO_DAVINCI_VPIF_CAPTURE
|
||||
tristate "DM646x/DA850/OMAPL138 EVM Video Capture"
|
||||
depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select VIDEO_DAVINCI_VPIF
|
||||
help
|
||||
Support for DM6467 based capture device.
|
||||
Enables Davinci VPIF module used for captur devices.
|
||||
This module is common for following DM6467/DA850/OMAPL138
|
||||
based capture devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vpif_capture.
|
||||
|
||||
config VIDEO_DAVINCI_VPIF
|
||||
tristate "DaVinci VPIF Driver"
|
||||
depends on DISPLAY_DAVINCI_DM646X_EVM
|
||||
depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE
|
||||
help
|
||||
Support for DaVinci VPIF Driver.
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
# VPIF
|
||||
obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o
|
||||
|
||||
#DM646x EVM Display driver
|
||||
obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o
|
||||
#DM646x EVM Capture driver
|
||||
obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o
|
||||
#VPIF Display driver
|
||||
obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif_display.o
|
||||
#VPIF Capture driver
|
||||
obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif_capture.o
|
||||
|
||||
# Capture: DM6446 and DM355
|
||||
obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o
|
||||
|
|
|
@ -1083,7 +1083,7 @@ vpbe_display_s_dv_preset(struct file *file, void *priv,
|
|||
}
|
||||
|
||||
/* Set the given standard in the encoder */
|
||||
if (NULL != vpbe_dev->ops.s_dv_preset)
|
||||
if (!vpbe_dev->ops.s_dv_preset)
|
||||
return -EINVAL;
|
||||
|
||||
ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
|
||||
|
@ -1517,6 +1517,8 @@ static int vpbe_display_g_register(struct file *file, void *priv,
|
|||
struct v4l2_dbg_register *reg)
|
||||
{
|
||||
struct v4l2_dbg_match *match = ®->match;
|
||||
struct vpbe_fh *fh = file->private_data;
|
||||
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
|
||||
|
||||
if (match->type >= 2) {
|
||||
v4l2_subdev_call(vpbe_dev->venc,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* vpif - DM646x Video Port Interface driver
|
||||
* vpif - Video Port Interface driver
|
||||
* VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
|
||||
* that receiveing video byte stream and two channels(2, 3) for video output.
|
||||
* The hardware supports SDTV, HDTV formats, raw data capture.
|
||||
|
@ -23,6 +23,8 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#include "vpif.h"
|
||||
|
@ -40,6 +42,7 @@ static struct resource *res;
|
|||
spinlock_t vpif_lock;
|
||||
|
||||
void __iomem *vpif_base;
|
||||
struct clk *vpif_clk;
|
||||
|
||||
/**
|
||||
* ch_params: video standard configuration parameters for vpif
|
||||
|
@ -346,7 +349,7 @@ static void config_vpif_params(struct vpif_params *vpifparams,
|
|||
|
||||
value = regr(reg);
|
||||
/* Set data width */
|
||||
value &= ((~(unsigned int)(0x3)) <<
|
||||
value &= ~(0x3u <<
|
||||
VPIF_CH_DATA_WIDTH_BIT);
|
||||
value |= ((vpifparams->params.data_sz) <<
|
||||
VPIF_CH_DATA_WIDTH_BIT);
|
||||
|
@ -434,10 +437,19 @@ static int __init vpif_probe(struct platform_device *pdev)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
vpif_clk = clk_get(&pdev->dev, "vpif");
|
||||
if (IS_ERR(vpif_clk)) {
|
||||
status = PTR_ERR(vpif_clk);
|
||||
goto clk_fail;
|
||||
}
|
||||
clk_enable(vpif_clk);
|
||||
|
||||
spin_lock_init(&vpif_lock);
|
||||
dev_info(&pdev->dev, "vpif probe success\n");
|
||||
return 0;
|
||||
|
||||
clk_fail:
|
||||
iounmap(vpif_base);
|
||||
fail:
|
||||
release_mem_region(res->start, res_len);
|
||||
return status;
|
||||
|
@ -445,15 +457,44 @@ fail:
|
|||
|
||||
static int __devexit vpif_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (vpif_clk) {
|
||||
clk_disable(vpif_clk);
|
||||
clk_put(vpif_clk);
|
||||
}
|
||||
|
||||
iounmap(vpif_base);
|
||||
release_mem_region(res->start, res_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int vpif_suspend(struct device *dev)
|
||||
{
|
||||
clk_disable(vpif_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpif_resume(struct device *dev)
|
||||
{
|
||||
clk_enable(vpif_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops vpif_pm = {
|
||||
.suspend = vpif_suspend,
|
||||
.resume = vpif_resume,
|
||||
};
|
||||
|
||||
#define vpif_pm_ops (&vpif_pm)
|
||||
#else
|
||||
#define vpif_pm_ops NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver vpif_driver = {
|
||||
.driver = {
|
||||
.name = "vpif",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = vpif_pm_ops,
|
||||
},
|
||||
.remove = __devexit_p(vpif_remove),
|
||||
.probe = vpif_probe,
|
||||
|
|
|
@ -211,6 +211,12 @@ static inline void vpif_clr_bit(u32 reg, u32 bit)
|
|||
#define VPIF_CH3_INT_CTRL_SHIFT (6)
|
||||
#define VPIF_CH_INT_CTRL_SHIFT (6)
|
||||
|
||||
#define VPIF_CH2_CLIP_ANC_EN 14
|
||||
#define VPIF_CH2_CLIP_ACTIVE_EN 13
|
||||
|
||||
#define VPIF_CH3_CLIP_ANC_EN 14
|
||||
#define VPIF_CH3_CLIP_ACTIVE_EN 13
|
||||
|
||||
/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
|
||||
#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
|
||||
(VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
|
||||
|
@ -515,6 +521,30 @@ static inline void channel3_raw_enable(int enable, u8 index)
|
|||
vpif_clr_bit(VPIF_CH3_CTRL, mask);
|
||||
}
|
||||
|
||||
/* function to enable clipping (for both active and blanking regions) on ch 2 */
|
||||
static inline void channel2_clipping_enable(int enable)
|
||||
{
|
||||
if (enable) {
|
||||
vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
|
||||
vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
|
||||
} else {
|
||||
vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
|
||||
vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
|
||||
}
|
||||
}
|
||||
|
||||
/* function to enable clipping (for both active and blanking regions) on ch 2 */
|
||||
static inline void channel3_clipping_enable(int enable)
|
||||
{
|
||||
if (enable) {
|
||||
vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
|
||||
vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
|
||||
} else {
|
||||
vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
|
||||
vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
|
||||
}
|
||||
}
|
||||
|
||||
/* inline function to set buffer addresses in case of Y/C non mux mode */
|
||||
static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
|
||||
unsigned long btm_strt_luma,
|
||||
|
@ -569,6 +599,21 @@ static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
|
|||
regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
|
||||
}
|
||||
|
||||
static inline int vpif_intr_status(int channel)
|
||||
{
|
||||
int status = 0;
|
||||
int mask;
|
||||
|
||||
if (channel < 0 || channel > 3)
|
||||
return 0;
|
||||
|
||||
mask = 1 << channel;
|
||||
status = regr(VPIF_STATUS) & mask;
|
||||
regw(status, VPIF_STATUS_CLR);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#define VPIF_MAX_NAME (30)
|
||||
|
||||
/* This structure will store size parameters as per the mode selected by user */
|
||||
|
|
|
@ -80,108 +80,45 @@ static struct vpif_config_params config_params = {
|
|||
/* global variables */
|
||||
static struct vpif_device vpif_obj = { {NULL} };
|
||||
static struct device *vpif_dev;
|
||||
|
||||
/**
|
||||
* vpif_uservirt_to_phys : translate user/virtual address to phy address
|
||||
* @virtp: user/virtual address
|
||||
*
|
||||
* This inline function is used to convert user space virtual address to
|
||||
* physical address.
|
||||
*/
|
||||
static inline u32 vpif_uservirt_to_phys(u32 virtp)
|
||||
{
|
||||
unsigned long physp = 0;
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
vma = find_vma(mm, virtp);
|
||||
|
||||
/* For kernel direct-mapped memory, take the easy way */
|
||||
if (virtp >= PAGE_OFFSET)
|
||||
physp = virt_to_phys((void *)virtp);
|
||||
else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff))
|
||||
/**
|
||||
* this will catch, kernel-allocated, mmaped-to-usermode
|
||||
* addresses
|
||||
*/
|
||||
physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
|
||||
else {
|
||||
/* otherwise, use get_user_pages() for general userland pages */
|
||||
int res, nr_pages = 1;
|
||||
struct page *pages;
|
||||
|
||||
down_read(¤t->mm->mmap_sem);
|
||||
|
||||
res = get_user_pages(current, current->mm,
|
||||
virtp, nr_pages, 1, 0, &pages, NULL);
|
||||
up_read(¤t->mm->mmap_sem);
|
||||
|
||||
if (res == nr_pages)
|
||||
physp = __pa(page_address(&pages[0]) +
|
||||
(virtp & ~PAGE_MASK));
|
||||
else {
|
||||
vpif_err("get_user_pages failed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return physp;
|
||||
}
|
||||
static void vpif_calculate_offsets(struct channel_obj *ch);
|
||||
static void vpif_config_addr(struct channel_obj *ch, int muxmode);
|
||||
|
||||
/**
|
||||
* buffer_prepare : callback function for buffer prepare
|
||||
* @q : buffer queue ptr
|
||||
* @vb: ptr to video buffer
|
||||
* @field: field info
|
||||
* @vb: ptr to vb2_buffer
|
||||
*
|
||||
* This is the callback function for buffer prepare when videobuf_qbuf()
|
||||
* 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 vpif_buffer_prepare(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb,
|
||||
enum v4l2_field field)
|
||||
static int vpif_buffer_prepare(struct vb2_buffer *vb)
|
||||
{
|
||||
/* Get the file handle object and channel object */
|
||||
struct vpif_fh *fh = q->priv_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct vb2_queue *q = vb->vb2_queue;
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
unsigned long addr;
|
||||
|
||||
|
||||
vpif_dbg(2, debug, "vpif_buffer_prepare\n");
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
/* If buffer is not initialized, initialize it */
|
||||
if (VIDEOBUF_NEEDS_INIT == vb->state) {
|
||||
vb->width = common->width;
|
||||
vb->height = common->height;
|
||||
vb->size = vb->width * vb->height;
|
||||
vb->field = field;
|
||||
}
|
||||
vb->state = VIDEOBUF_PREPARED;
|
||||
/**
|
||||
* if user pointer memory mechanism is used, get the physical
|
||||
* address of the buffer
|
||||
*/
|
||||
if (V4L2_MEMORY_USERPTR == common->memory) {
|
||||
if (0 == vb->baddr) {
|
||||
vpif_dbg(1, debug, "buffer address is 0\n");
|
||||
return -EINVAL;
|
||||
if (vb->state != VB2_BUF_STATE_ACTIVE &&
|
||||
vb->state != VB2_BUF_STATE_PREPARED) {
|
||||
vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
|
||||
if (vb2_plane_vaddr(vb, 0) &&
|
||||
vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
|
||||
goto exit;
|
||||
addr = vb2_dma_contig_plane_dma_addr(vb, 0);
|
||||
|
||||
if (q->streaming) {
|
||||
if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
|
||||
!IS_ALIGNED((addr + common->ybtm_off), 8) ||
|
||||
!IS_ALIGNED((addr + common->ctop_off), 8) ||
|
||||
!IS_ALIGNED((addr + common->cbtm_off), 8))
|
||||
goto exit;
|
||||
}
|
||||
vb->boff = vpif_uservirt_to_phys(vb->baddr);
|
||||
if (!IS_ALIGNED(vb->boff, 8))
|
||||
goto exit;
|
||||
}
|
||||
|
||||
addr = vb->boff;
|
||||
if (q->streaming) {
|
||||
if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
|
||||
!IS_ALIGNED((addr + common->ybtm_off), 8) ||
|
||||
!IS_ALIGNED((addr + common->ctop_off), 8) ||
|
||||
!IS_ALIGNED((addr + common->cbtm_off), 8))
|
||||
goto exit;
|
||||
}
|
||||
return 0;
|
||||
exit:
|
||||
|
@ -190,49 +127,79 @@ exit:
|
|||
}
|
||||
|
||||
/**
|
||||
* vpif_buffer_setup : Callback function for buffer setup.
|
||||
* @q: buffer queue ptr
|
||||
* @count: number of buffers
|
||||
* @size: size of the buffer
|
||||
* vpif_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 count and buffer size
|
||||
*/
|
||||
static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
|
||||
unsigned int *size)
|
||||
static int vpif_buffer_queue_setup(struct vb2_queue *vq,
|
||||
const struct v4l2_format *fmt,
|
||||
unsigned int *nbuffers, unsigned int *nplanes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
{
|
||||
/* Get the file handle object and channel object */
|
||||
struct vpif_fh *fh = q->priv_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
unsigned long size;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
vpif_dbg(2, debug, "vpif_buffer_setup\n");
|
||||
|
||||
/* If memory type is not mmap, return */
|
||||
if (V4L2_MEMORY_MMAP != common->memory)
|
||||
return 0;
|
||||
if (V4L2_MEMORY_MMAP == common->memory) {
|
||||
/* Calculate the size of the buffer */
|
||||
size = config_params.channel_bufsize[ch->channel_id];
|
||||
/*
|
||||
* Checking if the buffer size exceeds the available buffer
|
||||
* ycmux_mode = 0 means 1 channel mode HD and
|
||||
* ycmux_mode = 1 means 2 channels mode SD
|
||||
*/
|
||||
if (ch->vpifparams.std_info.ycmux_mode == 0) {
|
||||
if (config_params.video_limit[ch->channel_id])
|
||||
while (size * *nbuffers >
|
||||
(config_params.video_limit[0]
|
||||
+ config_params.video_limit[1]))
|
||||
(*nbuffers)--;
|
||||
} else {
|
||||
if (config_params.video_limit[ch->channel_id])
|
||||
while (size * *nbuffers >
|
||||
config_params.video_limit[ch->channel_id])
|
||||
(*nbuffers)--;
|
||||
}
|
||||
|
||||
/* Calculate the size of the buffer */
|
||||
*size = config_params.channel_bufsize[ch->channel_id];
|
||||
} else {
|
||||
size = common->fmt.fmt.pix.sizeimage;
|
||||
}
|
||||
|
||||
if (*nbuffers < config_params.min_numbuffers)
|
||||
*nbuffers = config_params.min_numbuffers;
|
||||
|
||||
*nplanes = 1;
|
||||
sizes[0] = size;
|
||||
alloc_ctxs[0] = common->alloc_ctx;
|
||||
|
||||
if (*count < config_params.min_numbuffers)
|
||||
*count = config_params.min_numbuffers;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vpif_buffer_queue : Callback function to add buffer to DMA queue
|
||||
* @q: ptr to videobuf_queue
|
||||
* @vb: ptr to videobuf_buffer
|
||||
* @vb: ptr to vb2_buffer
|
||||
*/
|
||||
static void vpif_buffer_queue(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb)
|
||||
static void vpif_buffer_queue(struct vb2_buffer *vb)
|
||||
{
|
||||
/* Get the file handle object and channel object */
|
||||
struct vpif_fh *fh = q->priv_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct vpif_cap_buffer *buf = container_of(vb,
|
||||
struct vpif_cap_buffer, vb);
|
||||
struct common_obj *common;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
@ -240,43 +207,189 @@ static void vpif_buffer_queue(struct videobuf_queue *q,
|
|||
vpif_dbg(2, debug, "vpif_buffer_queue\n");
|
||||
|
||||
/* add the buffer to the DMA queue */
|
||||
list_add_tail(&vb->queue, &common->dma_queue);
|
||||
/* Change state of the buffer */
|
||||
vb->state = VIDEOBUF_QUEUED;
|
||||
list_add_tail(&buf->list, &common->dma_queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* vpif_buffer_release : Callback function to free buffer
|
||||
* @q: buffer queue ptr
|
||||
* @vb: ptr to video buffer
|
||||
* vpif_buf_cleanup : Callback function to free buffer
|
||||
* @vb: ptr to vb2_buffer
|
||||
*
|
||||
* This function is called from the videobuf layer to free memory
|
||||
* This function is called from the videobuf2 layer to free memory
|
||||
* allocated to the buffers
|
||||
*/
|
||||
static void vpif_buffer_release(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb)
|
||||
static void vpif_buf_cleanup(struct vb2_buffer *vb)
|
||||
{
|
||||
/* Get the file handle object and channel object */
|
||||
struct vpif_fh *fh = q->priv_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct vpif_cap_buffer *buf = container_of(vb,
|
||||
struct vpif_cap_buffer, vb);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
unsigned long flags;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
spin_lock_irqsave(&common->irqlock, flags);
|
||||
if (vb->state == VB2_BUF_STATE_ACTIVE)
|
||||
list_del_init(&buf->list);
|
||||
spin_unlock_irqrestore(&common->irqlock, flags);
|
||||
|
||||
}
|
||||
|
||||
static void vpif_wait_prepare(struct vb2_queue *vq)
|
||||
{
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
videobuf_dma_contig_free(q, vb);
|
||||
vb->state = VIDEOBUF_NEEDS_INIT;
|
||||
mutex_unlock(&common->lock);
|
||||
}
|
||||
|
||||
static struct videobuf_queue_ops video_qops = {
|
||||
.buf_setup = vpif_buffer_setup,
|
||||
.buf_prepare = vpif_buffer_prepare,
|
||||
.buf_queue = vpif_buffer_queue,
|
||||
.buf_release = vpif_buffer_release,
|
||||
};
|
||||
static void vpif_wait_finish(struct vb2_queue *vq)
|
||||
{
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
mutex_lock(&common->lock);
|
||||
}
|
||||
|
||||
static int vpif_buffer_init(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vpif_cap_buffer *buf = container_of(vb,
|
||||
struct vpif_cap_buffer, vb);
|
||||
|
||||
INIT_LIST_HEAD(&buf->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
|
||||
{ {1, 1} };
|
||||
|
||||
static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
|
||||
{
|
||||
struct vpif_capture_config *vpif_config_data =
|
||||
vpif_dev->platform_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
struct vpif_params *vpif = &ch->vpifparams;
|
||||
unsigned long addr = 0;
|
||||
int ret;
|
||||
|
||||
/* If buffer queue is empty, return error */
|
||||
if (list_empty(&common->dma_queue)) {
|
||||
vpif_dbg(1, debug, "buffer queue is empty\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get the next frame from the buffer queue */
|
||||
common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
|
||||
struct vpif_cap_buffer, list);
|
||||
/* Remove buffer from the buffer queue */
|
||||
list_del(&common->cur_frm->list);
|
||||
/* Mark state of the current frame to active */
|
||||
common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
|
||||
/* Initialize field_id and started member */
|
||||
ch->field_id = 0;
|
||||
common->started = 1;
|
||||
addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
|
||||
|
||||
/* Calculate the offset for Y and C data in the buffer */
|
||||
vpif_calculate_offsets(ch);
|
||||
|
||||
if ((vpif->std_info.frm_fmt &&
|
||||
((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
|
||||
(common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
|
||||
(!vpif->std_info.frm_fmt &&
|
||||
(common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
|
||||
vpif_dbg(1, debug, "conflict in field format and std format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* configure 1 or 2 channel mode */
|
||||
ret = vpif_config_data->setup_input_channel_mode
|
||||
(vpif->std_info.ycmux_mode);
|
||||
|
||||
if (ret < 0) {
|
||||
vpif_dbg(1, debug, "can't set vpif channel mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Call vpif_set_params function to set the parameters and addresses */
|
||||
ret = vpif_set_video_params(vpif, ch->channel_id);
|
||||
|
||||
if (ret < 0) {
|
||||
vpif_dbg(1, debug, "can't set video params\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
common->started = ret;
|
||||
vpif_config_addr(ch, ret);
|
||||
|
||||
common->set_addr(addr + common->ytop_off,
|
||||
addr + common->ybtm_off,
|
||||
addr + common->ctop_off,
|
||||
addr + common->cbtm_off);
|
||||
|
||||
/**
|
||||
* Set interrupt for both the fields in VPIF Register enable channel in
|
||||
* VPIF register
|
||||
*/
|
||||
if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
|
||||
channel0_intr_assert();
|
||||
channel0_intr_enable(1);
|
||||
enable_channel0(1);
|
||||
}
|
||||
if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
|
||||
(common->started == 2)) {
|
||||
channel1_intr_assert();
|
||||
channel1_intr_enable(1);
|
||||
enable_channel1(1);
|
||||
}
|
||||
channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* abort streaming and wait for last buffer */
|
||||
static int vpif_stop_streaming(struct vb2_queue *vq)
|
||||
{
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
|
||||
if (!vb2_is_streaming(vq))
|
||||
return 0;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
/* release all active buffers */
|
||||
while (!list_empty(&common->dma_queue)) {
|
||||
common->next_frm = list_entry(common->dma_queue.next,
|
||||
struct vpif_cap_buffer, list);
|
||||
list_del(&common->next_frm->list);
|
||||
vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vb2_ops video_qops = {
|
||||
.queue_setup = vpif_buffer_queue_setup,
|
||||
.wait_prepare = vpif_wait_prepare,
|
||||
.wait_finish = vpif_wait_finish,
|
||||
.buf_init = vpif_buffer_init,
|
||||
.buf_prepare = vpif_buffer_prepare,
|
||||
.start_streaming = vpif_start_streaming,
|
||||
.stop_streaming = vpif_stop_streaming,
|
||||
.buf_cleanup = vpif_buf_cleanup,
|
||||
.buf_queue = vpif_buffer_queue,
|
||||
};
|
||||
|
||||
/**
|
||||
* vpif_process_buffer_complete: process a completed buffer
|
||||
* @common: ptr to common channel object
|
||||
|
@ -287,9 +400,9 @@ static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
|
|||
*/
|
||||
static void vpif_process_buffer_complete(struct common_obj *common)
|
||||
{
|
||||
do_gettimeofday(&common->cur_frm->ts);
|
||||
common->cur_frm->state = VIDEOBUF_DONE;
|
||||
wake_up_interruptible(&common->cur_frm->done);
|
||||
do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
|
||||
vb2_buffer_done(&common->cur_frm->vb,
|
||||
VB2_BUF_STATE_DONE);
|
||||
/* Make curFrm pointing to nextFrm */
|
||||
common->cur_frm = common->next_frm;
|
||||
}
|
||||
|
@ -307,14 +420,11 @@ static void vpif_schedule_next_buffer(struct common_obj *common)
|
|||
unsigned long addr = 0;
|
||||
|
||||
common->next_frm = list_entry(common->dma_queue.next,
|
||||
struct videobuf_buffer, queue);
|
||||
struct vpif_cap_buffer, list);
|
||||
/* Remove that buffer from the buffer queue */
|
||||
list_del(&common->next_frm->queue);
|
||||
common->next_frm->state = VIDEOBUF_ACTIVE;
|
||||
if (V4L2_MEMORY_USERPTR == common->memory)
|
||||
addr = common->next_frm->boff;
|
||||
else
|
||||
addr = videobuf_to_dma_contig(common->next_frm);
|
||||
list_del(&common->next_frm->list);
|
||||
common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
|
||||
addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
|
||||
|
||||
/* Set top and bottom field addresses in VPIF registers */
|
||||
common->set_addr(addr + common->ytop_off,
|
||||
|
@ -341,6 +451,9 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
|
|||
int fid = -1, i;
|
||||
|
||||
channel_id = *(int *)(dev_id);
|
||||
if (!vpif_intr_status(channel_id))
|
||||
return IRQ_NONE;
|
||||
|
||||
ch = dev->dev[channel_id];
|
||||
|
||||
field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
|
||||
|
@ -485,10 +598,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
|
|||
} else
|
||||
vid_ch->buf_field = common->fmt.fmt.pix.field;
|
||||
|
||||
if (V4L2_MEMORY_USERPTR == common->memory)
|
||||
sizeimage = common->fmt.fmt.pix.sizeimage;
|
||||
else
|
||||
sizeimage = config_params.channel_bufsize[ch->channel_id];
|
||||
sizeimage = common->fmt.fmt.pix.sizeimage;
|
||||
|
||||
hpitch = common->fmt.fmt.pix.bytesperline;
|
||||
vpitch = sizeimage / (hpitch * 2);
|
||||
|
@ -640,10 +750,7 @@ static int vpif_check_format(struct channel_obj *ch,
|
|||
hpitch = vpif_params->std_info.width;
|
||||
}
|
||||
|
||||
if (V4L2_MEMORY_USERPTR == common->memory)
|
||||
sizeimage = pixfmt->sizeimage;
|
||||
else
|
||||
sizeimage = config_params.channel_bufsize[ch->channel_id];
|
||||
sizeimage = pixfmt->sizeimage;
|
||||
|
||||
vpitch = sizeimage / (hpitch * 2);
|
||||
|
||||
|
@ -703,7 +810,7 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)
|
|||
}
|
||||
|
||||
/**
|
||||
* vpfe_mmap : It is used to map kernel space buffers into user spaces
|
||||
* vpif_mmap : It is used to map kernel space buffers into user spaces
|
||||
* @filep: file pointer
|
||||
* @vma: ptr to vm_area_struct
|
||||
*/
|
||||
|
@ -716,7 +823,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
|
|||
|
||||
vpif_dbg(2, debug, "vpif_mmap\n");
|
||||
|
||||
return videobuf_mmap_mapper(&common->buffer_queue, vma);
|
||||
return vb2_mmap(&common->buffer_queue, vma);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -733,7 +840,7 @@ static unsigned int vpif_poll(struct file *filep, poll_table * wait)
|
|||
vpif_dbg(2, debug, "vpif_poll\n");
|
||||
|
||||
if (common->started)
|
||||
return videobuf_poll_stream(filep, &common->buffer_queue, wait);
|
||||
return vb2_poll(&common->buffer_queue, filep, wait);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -812,7 +919,7 @@ static int vpif_open(struct file *filep)
|
|||
* vpif_release : function to clean up file close
|
||||
* @filep: file pointer
|
||||
*
|
||||
* This function deletes buffer queue, frees the buffers and the vpfe file
|
||||
* This function deletes buffer queue, frees the buffers and the vpif file
|
||||
* handle
|
||||
*/
|
||||
static int vpif_release(struct file *filep)
|
||||
|
@ -841,8 +948,8 @@ static int vpif_release(struct file *filep)
|
|||
}
|
||||
common->started = 0;
|
||||
/* Free buffers allocated */
|
||||
videobuf_queue_cancel(&common->buffer_queue);
|
||||
videobuf_mmap_free(&common->buffer_queue);
|
||||
vb2_queue_release(&common->buffer_queue);
|
||||
vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
|
||||
}
|
||||
|
||||
/* Decrement channel usrs counter */
|
||||
|
@ -872,6 +979,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
|
|||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
u8 index = 0;
|
||||
struct vb2_queue *q;
|
||||
|
||||
vpif_dbg(2, debug, "vpif_reqbufs\n");
|
||||
|
||||
|
@ -887,7 +995,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
|
|||
}
|
||||
}
|
||||
|
||||
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type)
|
||||
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev)
|
||||
return -EINVAL;
|
||||
|
||||
index = VPIF_VIDEO_INDEX;
|
||||
|
@ -897,14 +1005,21 @@ static int vpif_reqbufs(struct file *file, void *priv,
|
|||
if (0 != common->io_usrs)
|
||||
return -EBUSY;
|
||||
|
||||
/* Initialize videobuf queue as per the buffer type */
|
||||
videobuf_queue_dma_contig_init(&common->buffer_queue,
|
||||
&video_qops, NULL,
|
||||
&common->irqlock,
|
||||
reqbuf->type,
|
||||
common->fmt.fmt.pix.field,
|
||||
sizeof(struct videobuf_buffer), fh,
|
||||
&common->lock);
|
||||
/* Initialize videobuf2 queue as per the buffer type */
|
||||
common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
|
||||
if (!common->alloc_ctx) {
|
||||
vpif_err("Failed to get the context\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
q = &common->buffer_queue;
|
||||
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
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 vpif_cap_buffer);
|
||||
|
||||
vb2_queue_init(q);
|
||||
|
||||
/* Set io allowed member of file handle to TRUE */
|
||||
fh->io_allowed[index] = 1;
|
||||
|
@ -915,7 +1030,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
|
|||
INIT_LIST_HEAD(&common->dma_queue);
|
||||
|
||||
/* Allocate buffers */
|
||||
return videobuf_reqbufs(&common->buffer_queue, reqbuf);
|
||||
return vb2_reqbufs(&common->buffer_queue, reqbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -941,7 +1056,7 @@ static int vpif_querybuf(struct file *file, void *priv,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
return videobuf_querybuf(&common->buffer_queue, buf);
|
||||
return vb2_querybuf(&common->buffer_queue, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -957,10 +1072,6 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
|||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
struct v4l2_buffer tbuf = *buf;
|
||||
struct videobuf_buffer *buf1;
|
||||
unsigned long addr = 0;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
vpif_dbg(2, debug, "vpif_qbuf\n");
|
||||
|
||||
|
@ -970,76 +1081,11 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
|||
}
|
||||
|
||||
if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
|
||||
vpif_err("fh io not allowed \n");
|
||||
vpif_err("fh io not allowed\n");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!(list_empty(&common->dma_queue)) ||
|
||||
(common->cur_frm != common->next_frm) ||
|
||||
!common->started ||
|
||||
(common->started && (0 == ch->field_id)))
|
||||
return videobuf_qbuf(&common->buffer_queue, buf);
|
||||
|
||||
/* bufferqueue is empty store buffer address in VPIF registers */
|
||||
mutex_lock(&common->buffer_queue.vb_lock);
|
||||
buf1 = common->buffer_queue.bufs[tbuf.index];
|
||||
|
||||
if ((buf1->state == VIDEOBUF_QUEUED) ||
|
||||
(buf1->state == VIDEOBUF_ACTIVE)) {
|
||||
vpif_err("invalid state\n");
|
||||
goto qbuf_exit;
|
||||
}
|
||||
|
||||
switch (buf1->memory) {
|
||||
case V4L2_MEMORY_MMAP:
|
||||
if (buf1->baddr == 0)
|
||||
goto qbuf_exit;
|
||||
break;
|
||||
|
||||
case V4L2_MEMORY_USERPTR:
|
||||
if (tbuf.length < buf1->bsize)
|
||||
goto qbuf_exit;
|
||||
|
||||
if ((VIDEOBUF_NEEDS_INIT != buf1->state)
|
||||
&& (buf1->baddr != tbuf.m.userptr)) {
|
||||
vpif_buffer_release(&common->buffer_queue, buf1);
|
||||
buf1->baddr = tbuf.m.userptr;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
goto qbuf_exit;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
|
||||
common->buffer_queue.field);
|
||||
if (ret < 0) {
|
||||
local_irq_restore(flags);
|
||||
goto qbuf_exit;
|
||||
}
|
||||
|
||||
buf1->state = VIDEOBUF_ACTIVE;
|
||||
|
||||
if (V4L2_MEMORY_USERPTR == common->memory)
|
||||
addr = buf1->boff;
|
||||
else
|
||||
addr = videobuf_to_dma_contig(buf1);
|
||||
|
||||
common->next_frm = buf1;
|
||||
common->set_addr(addr + common->ytop_off,
|
||||
addr + common->ybtm_off,
|
||||
addr + common->ctop_off,
|
||||
addr + common->cbtm_off);
|
||||
|
||||
local_irq_restore(flags);
|
||||
list_add_tail(&buf1->stream, &common->buffer_queue.stream);
|
||||
mutex_unlock(&common->buffer_queue.vb_lock);
|
||||
return 0;
|
||||
|
||||
qbuf_exit:
|
||||
mutex_unlock(&common->buffer_queue.vb_lock);
|
||||
return -EINVAL;
|
||||
return vb2_qbuf(&common->buffer_queue, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1056,8 +1102,8 @@ static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
|||
|
||||
vpif_dbg(2, debug, "vpif_dqbuf\n");
|
||||
|
||||
return videobuf_dqbuf(&common->buffer_queue, buf,
|
||||
file->f_flags & O_NONBLOCK);
|
||||
return vb2_dqbuf(&common->buffer_queue, buf,
|
||||
(file->f_flags & O_NONBLOCK));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1070,13 +1116,11 @@ static int vpif_streamon(struct file *file, void *priv,
|
|||
enum v4l2_buf_type buftype)
|
||||
{
|
||||
|
||||
struct vpif_capture_config *config = vpif_dev->platform_data;
|
||||
struct vpif_fh *fh = priv;
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
|
||||
struct vpif_params *vpif;
|
||||
unsigned long addr = 0;
|
||||
int ret = 0;
|
||||
|
||||
vpif_dbg(2, debug, "vpif_streamon\n");
|
||||
|
@ -1122,95 +1166,13 @@ static int vpif_streamon(struct file *file, void *priv,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Call videobuf_streamon to start streaming in videobuf */
|
||||
ret = videobuf_streamon(&common->buffer_queue);
|
||||
/* Call vb2_streamon to start streaming in videobuf2 */
|
||||
ret = vb2_streamon(&common->buffer_queue, buftype);
|
||||
if (ret) {
|
||||
vpif_dbg(1, debug, "videobuf_streamon\n");
|
||||
vpif_dbg(1, debug, "vb2_streamon\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If buffer queue is empty, return error */
|
||||
if (list_empty(&common->dma_queue)) {
|
||||
vpif_dbg(1, debug, "buffer queue is empty\n");
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Get the next frame from the buffer queue */
|
||||
common->cur_frm = list_entry(common->dma_queue.next,
|
||||
struct videobuf_buffer, queue);
|
||||
common->next_frm = common->cur_frm;
|
||||
|
||||
/* Remove buffer from the buffer queue */
|
||||
list_del(&common->cur_frm->queue);
|
||||
/* Mark state of the current frame to active */
|
||||
common->cur_frm->state = VIDEOBUF_ACTIVE;
|
||||
/* Initialize field_id and started member */
|
||||
ch->field_id = 0;
|
||||
common->started = 1;
|
||||
|
||||
if (V4L2_MEMORY_USERPTR == common->memory)
|
||||
addr = common->cur_frm->boff;
|
||||
else
|
||||
addr = videobuf_to_dma_contig(common->cur_frm);
|
||||
|
||||
/* Calculate the offset for Y and C data in the buffer */
|
||||
vpif_calculate_offsets(ch);
|
||||
|
||||
if ((vpif->std_info.frm_fmt &&
|
||||
((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
|
||||
(common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
|
||||
(!vpif->std_info.frm_fmt &&
|
||||
(common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
|
||||
vpif_dbg(1, debug, "conflict in field format and std format\n");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* configure 1 or 2 channel mode */
|
||||
ret = config->setup_input_channel_mode(vpif->std_info.ycmux_mode);
|
||||
|
||||
if (ret < 0) {
|
||||
vpif_dbg(1, debug, "can't set vpif channel mode\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Call vpif_set_params function to set the parameters and addresses */
|
||||
ret = vpif_set_video_params(vpif, ch->channel_id);
|
||||
|
||||
if (ret < 0) {
|
||||
vpif_dbg(1, debug, "can't set video params\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
common->started = ret;
|
||||
vpif_config_addr(ch, ret);
|
||||
|
||||
common->set_addr(addr + common->ytop_off,
|
||||
addr + common->ybtm_off,
|
||||
addr + common->ctop_off,
|
||||
addr + common->cbtm_off);
|
||||
|
||||
/**
|
||||
* Set interrupt for both the fields in VPIF Register enable channel in
|
||||
* VPIF register
|
||||
*/
|
||||
if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
|
||||
channel0_intr_assert();
|
||||
channel0_intr_enable(1);
|
||||
enable_channel0(1);
|
||||
}
|
||||
if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
|
||||
(common->started == 2)) {
|
||||
channel1_intr_assert();
|
||||
channel1_intr_enable(1);
|
||||
enable_channel1(1);
|
||||
}
|
||||
channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
|
||||
return ret;
|
||||
|
||||
exit:
|
||||
videobuf_streamoff(&common->buffer_queue);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1265,7 +1227,7 @@ static int vpif_streamoff(struct file *file, void *priv,
|
|||
if (ret && (ret != -ENOIOCTLCMD))
|
||||
vpif_dbg(1, debug, "stream off failed in subdev\n");
|
||||
|
||||
return videobuf_streamoff(&common->buffer_queue);
|
||||
return vb2_streamoff(&common->buffer_queue, buftype);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1679,7 +1641,7 @@ static int vpif_querycap(struct file *file, void *priv,
|
|||
|
||||
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
|
||||
strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
|
||||
strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info));
|
||||
strlcpy(cap->bus_info, "VPIF Platform", sizeof(cap->bus_info));
|
||||
strlcpy(cap->card, config->card_name, sizeof(cap->card));
|
||||
|
||||
return 0;
|
||||
|
@ -2168,6 +2130,7 @@ static __init int vpif_probe(struct platform_device *pdev)
|
|||
struct video_device *vfd;
|
||||
struct resource *res;
|
||||
int subdev_count;
|
||||
size_t size;
|
||||
|
||||
vpif_dev = &pdev->dev;
|
||||
|
||||
|
@ -2186,8 +2149,8 @@ static __init int vpif_probe(struct platform_device *pdev)
|
|||
k = 0;
|
||||
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
|
||||
for (i = res->start; i <= res->end; i++) {
|
||||
if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
|
||||
"DM646x_Capture",
|
||||
if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
|
||||
"VPIF_Capture",
|
||||
(void *)(&vpif_obj.dev[k]->channel_id))) {
|
||||
err = -EBUSY;
|
||||
i--;
|
||||
|
@ -2216,12 +2179,29 @@ static __init int vpif_probe(struct platform_device *pdev)
|
|||
vfd->v4l2_dev = &vpif_obj.v4l2_dev;
|
||||
vfd->release = video_device_release;
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"DM646x_VPIFCapture_DRIVER_V%s",
|
||||
"VPIF_Capture_DRIVER_V%s",
|
||||
VPIF_CAPTURE_VERSION);
|
||||
/* Set video_dev to the video device */
|
||||
ch->video_dev = vfd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res) {
|
||||
size = resource_size(res);
|
||||
/* The resources are divided into two equal memory and when we
|
||||
* have HD output we can add them together
|
||||
*/
|
||||
for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
|
||||
ch = vpif_obj.dev[j];
|
||||
ch->channel_id = j;
|
||||
/* only enabled if second resource exists */
|
||||
config_params.video_limit[ch->channel_id] = 0;
|
||||
if (size)
|
||||
config_params.video_limit[ch->channel_id] =
|
||||
size/2;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
|
||||
ch = vpif_obj.dev[j];
|
||||
ch->channel_id = j;
|
||||
|
@ -2275,8 +2255,7 @@ static __init int vpif_probe(struct platform_device *pdev)
|
|||
vpif_obj.sd[i]->grp_id = 1 << i;
|
||||
}
|
||||
|
||||
v4l2_info(&vpif_obj.v4l2_dev,
|
||||
"DM646x VPIF capture driver initialized\n");
|
||||
v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
|
||||
return 0;
|
||||
|
||||
probe_subdev_out:
|
||||
|
@ -2333,26 +2312,70 @@ static int vpif_remove(struct platform_device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/**
|
||||
* vpif_suspend: vpif device suspend
|
||||
*
|
||||
* TODO: Add suspend code here
|
||||
*/
|
||||
static int
|
||||
vpif_suspend(struct device *dev)
|
||||
static int vpif_suspend(struct device *dev)
|
||||
{
|
||||
return -1;
|
||||
|
||||
struct common_obj *common;
|
||||
struct channel_obj *ch;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
|
||||
/* Get the pointer to the channel object */
|
||||
ch = vpif_obj.dev[i];
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
mutex_lock(&common->lock);
|
||||
if (ch->usrs && common->io_usrs) {
|
||||
/* Disable channel */
|
||||
if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
|
||||
enable_channel0(0);
|
||||
channel0_intr_enable(0);
|
||||
}
|
||||
if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
|
||||
common->started == 2) {
|
||||
enable_channel1(0);
|
||||
channel1_intr_enable(0);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&common->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* vpif_resume: vpif device suspend
|
||||
*
|
||||
* TODO: Add resume code here
|
||||
*/
|
||||
static int
|
||||
vpif_resume(struct device *dev)
|
||||
static int vpif_resume(struct device *dev)
|
||||
{
|
||||
return -1;
|
||||
struct common_obj *common;
|
||||
struct channel_obj *ch;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
|
||||
/* Get the pointer to the channel object */
|
||||
ch = vpif_obj.dev[i];
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
mutex_lock(&common->lock);
|
||||
if (ch->usrs && common->io_usrs) {
|
||||
/* Disable channel */
|
||||
if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
|
||||
enable_channel0(1);
|
||||
channel0_intr_enable(1);
|
||||
}
|
||||
if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
|
||||
common->started == 2) {
|
||||
enable_channel1(1);
|
||||
channel1_intr_enable(1);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&common->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops vpif_dev_pm_ops = {
|
||||
|
@ -2360,11 +2383,16 @@ static const struct dev_pm_ops vpif_dev_pm_ops = {
|
|||
.resume = vpif_resume,
|
||||
};
|
||||
|
||||
#define vpif_pm_ops (&vpif_dev_pm_ops)
|
||||
#else
|
||||
#define vpif_pm_ops NULL
|
||||
#endif
|
||||
|
||||
static __refdata struct platform_driver vpif_driver = {
|
||||
.driver = {
|
||||
.name = "vpif_capture",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &vpif_dev_pm_ops,
|
||||
.pm = vpif_pm_ops,
|
||||
},
|
||||
.probe = vpif_probe,
|
||||
.remove = vpif_remove,
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/videobuf-core.h>
|
||||
#include <media/videobuf-dma-contig.h>
|
||||
#include <media/videobuf2-dma-contig.h>
|
||||
#include <media/davinci/vpif_types.h>
|
||||
|
||||
#include "vpif.h"
|
||||
|
@ -60,11 +60,16 @@ struct video_obj {
|
|||
u32 input_idx;
|
||||
};
|
||||
|
||||
struct vpif_cap_buffer {
|
||||
struct vb2_buffer vb;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct common_obj {
|
||||
/* Pointer pointing to current v4l2_buffer */
|
||||
struct videobuf_buffer *cur_frm;
|
||||
struct vpif_cap_buffer *cur_frm;
|
||||
/* Pointer pointing to current v4l2_buffer */
|
||||
struct videobuf_buffer *next_frm;
|
||||
struct vpif_cap_buffer *next_frm;
|
||||
/*
|
||||
* This field keeps track of type of buffer exchange mechanism
|
||||
* user has selected
|
||||
|
@ -73,7 +78,9 @@ struct common_obj {
|
|||
/* Used to store pixel format */
|
||||
struct v4l2_format fmt;
|
||||
/* Buffer queue used in video-buf */
|
||||
struct videobuf_queue buffer_queue;
|
||||
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;
|
||||
/* Used in video-buf */
|
||||
|
@ -151,6 +158,7 @@ struct vpif_config_params {
|
|||
u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
|
||||
u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
|
||||
u8 default_device[VPIF_CAPTURE_NUM_CHANNELS];
|
||||
u32 video_limit[VPIF_CAPTURE_NUM_CHANNELS];
|
||||
u8 max_device_type;
|
||||
};
|
||||
/* Struct which keeps track of the line numbers for the sliced vbi service */
|
||||
|
|
|
@ -46,7 +46,7 @@ MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
|
|||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(VPIF_DISPLAY_VERSION);
|
||||
|
||||
#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
|
||||
#define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
|
||||
|
||||
#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
|
||||
#define vpif_dbg(level, debug, fmt, arg...) \
|
||||
|
@ -82,89 +82,38 @@ static struct vpif_config_params config_params = {
|
|||
|
||||
static struct vpif_device vpif_obj = { {NULL} };
|
||||
static struct device *vpif_dev;
|
||||
static void vpif_calculate_offsets(struct channel_obj *ch);
|
||||
static void vpif_config_addr(struct channel_obj *ch, int muxmode);
|
||||
|
||||
/*
|
||||
* vpif_uservirt_to_phys: This function is used to convert user
|
||||
* space virtual address to physical address.
|
||||
*/
|
||||
static u32 vpif_uservirt_to_phys(u32 virtp)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned long physp = 0;
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
vma = find_vma(mm, virtp);
|
||||
|
||||
/* For kernel direct-mapped memory, take the easy way */
|
||||
if (virtp >= PAGE_OFFSET) {
|
||||
physp = virt_to_phys((void *)virtp);
|
||||
} else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) {
|
||||
/* this will catch, kernel-allocated, mmaped-to-usermode addr */
|
||||
physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
|
||||
} else {
|
||||
/* otherwise, use get_user_pages() for general userland pages */
|
||||
int res, nr_pages = 1;
|
||||
struct page *pages;
|
||||
down_read(¤t->mm->mmap_sem);
|
||||
|
||||
res = get_user_pages(current, current->mm,
|
||||
virtp, nr_pages, 1, 0, &pages, NULL);
|
||||
up_read(¤t->mm->mmap_sem);
|
||||
|
||||
if (res == nr_pages) {
|
||||
physp = __pa(page_address(&pages[0]) +
|
||||
(virtp & ~PAGE_MASK));
|
||||
} else {
|
||||
vpif_err("get_user_pages failed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return physp;
|
||||
}
|
||||
|
||||
/*
|
||||
* buffer_prepare: This is the callback function called from videobuf_qbuf()
|
||||
* buffer_prepare: This is the callback function called from vb2_qbuf()
|
||||
* function the buffer is prepared and user space virtual address is converted
|
||||
* into physical address
|
||||
*/
|
||||
static int vpif_buffer_prepare(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb,
|
||||
enum v4l2_field field)
|
||||
static int vpif_buffer_prepare(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vpif_fh *fh = q->priv_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct vb2_queue *q = vb->vb2_queue;
|
||||
struct common_obj *common;
|
||||
unsigned long addr;
|
||||
|
||||
common = &fh->channel->common[VPIF_VIDEO_INDEX];
|
||||
if (VIDEOBUF_NEEDS_INIT == vb->state) {
|
||||
vb->width = common->width;
|
||||
vb->height = common->height;
|
||||
vb->size = vb->width * vb->height;
|
||||
vb->field = field;
|
||||
}
|
||||
vb->state = VIDEOBUF_PREPARED;
|
||||
if (vb->state != VB2_BUF_STATE_ACTIVE &&
|
||||
vb->state != VB2_BUF_STATE_PREPARED) {
|
||||
vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
|
||||
if (vb2_plane_vaddr(vb, 0) &&
|
||||
vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
|
||||
goto buf_align_exit;
|
||||
|
||||
/* if user pointer memory mechanism is used, get the physical
|
||||
* address of the buffer */
|
||||
if (V4L2_MEMORY_USERPTR == common->memory) {
|
||||
if (!vb->baddr) {
|
||||
vpif_err("buffer_address is 0\n");
|
||||
return -EINVAL;
|
||||
addr = vb2_dma_contig_plane_dma_addr(vb, 0);
|
||||
if (q->streaming &&
|
||||
(V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
|
||||
if (!ISALIGNED(addr + common->ytop_off) ||
|
||||
!ISALIGNED(addr + common->ybtm_off) ||
|
||||
!ISALIGNED(addr + common->ctop_off) ||
|
||||
!ISALIGNED(addr + common->cbtm_off))
|
||||
goto buf_align_exit;
|
||||
}
|
||||
|
||||
vb->boff = vpif_uservirt_to_phys(vb->baddr);
|
||||
if (!ISALIGNED(vb->boff))
|
||||
goto buf_align_exit;
|
||||
}
|
||||
|
||||
addr = vb->boff;
|
||||
if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
|
||||
if (!ISALIGNED(addr + common->ytop_off) ||
|
||||
!ISALIGNED(addr + common->ybtm_off) ||
|
||||
!ISALIGNED(addr + common->ctop_off) ||
|
||||
!ISALIGNED(addr + common->cbtm_off))
|
||||
goto buf_align_exit;
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
@ -174,86 +123,255 @@ buf_align_exit:
|
|||
}
|
||||
|
||||
/*
|
||||
* vpif_buffer_setup: This function allocates memory for the buffers
|
||||
* vpif_buffer_queue_setup: This function allocates memory for the buffers
|
||||
*/
|
||||
static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
|
||||
unsigned int *size)
|
||||
static int vpif_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 vpif_fh *fh = q->priv_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
unsigned long size;
|
||||
|
||||
if (V4L2_MEMORY_MMAP != common->memory)
|
||||
return 0;
|
||||
if (V4L2_MEMORY_MMAP == common->memory) {
|
||||
size = config_params.channel_bufsize[ch->channel_id];
|
||||
/*
|
||||
* Checking if the buffer size exceeds the available buffer
|
||||
* ycmux_mode = 0 means 1 channel mode HD and
|
||||
* ycmux_mode = 1 means 2 channels mode SD
|
||||
*/
|
||||
if (ch->vpifparams.std_info.ycmux_mode == 0) {
|
||||
if (config_params.video_limit[ch->channel_id])
|
||||
while (size * *nbuffers >
|
||||
(config_params.video_limit[0]
|
||||
+ config_params.video_limit[1]))
|
||||
(*nbuffers)--;
|
||||
} else {
|
||||
if (config_params.video_limit[ch->channel_id])
|
||||
while (size * *nbuffers >
|
||||
config_params.video_limit[ch->channel_id])
|
||||
(*nbuffers)--;
|
||||
}
|
||||
} else {
|
||||
size = common->fmt.fmt.pix.sizeimage;
|
||||
}
|
||||
|
||||
*size = config_params.channel_bufsize[ch->channel_id];
|
||||
if (*count < config_params.min_numbuffers)
|
||||
*count = config_params.min_numbuffers;
|
||||
if (*nbuffers < config_params.min_numbuffers)
|
||||
*nbuffers = config_params.min_numbuffers;
|
||||
|
||||
*nplanes = 1;
|
||||
sizes[0] = size;
|
||||
alloc_ctxs[0] = common->alloc_ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* vpif_buffer_queue: This function adds the buffer to DMA queue
|
||||
*/
|
||||
static void vpif_buffer_queue(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb)
|
||||
static void vpif_buffer_queue(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vpif_fh *fh = q->priv_data;
|
||||
struct common_obj *common;
|
||||
|
||||
common = &fh->channel->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
/* add the buffer to the DMA queue */
|
||||
list_add_tail(&vb->queue, &common->dma_queue);
|
||||
vb->state = VIDEOBUF_QUEUED;
|
||||
}
|
||||
|
||||
/*
|
||||
* vpif_buffer_release: This function is called from the videobuf layer to
|
||||
* free memory allocated to the buffers
|
||||
*/
|
||||
static void vpif_buffer_release(struct videobuf_queue *q,
|
||||
struct videobuf_buffer *vb)
|
||||
{
|
||||
struct vpif_fh *fh = q->priv_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct vpif_disp_buffer *buf = container_of(vb,
|
||||
struct vpif_disp_buffer, vb);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
unsigned int buf_size = 0;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
videobuf_dma_contig_free(q, vb);
|
||||
vb->state = VIDEOBUF_NEEDS_INIT;
|
||||
|
||||
if (V4L2_MEMORY_MMAP != common->memory)
|
||||
return;
|
||||
|
||||
buf_size = config_params.channel_bufsize[ch->channel_id];
|
||||
/* add the buffer to the DMA queue */
|
||||
list_add_tail(&buf->list, &common->dma_queue);
|
||||
}
|
||||
|
||||
/*
|
||||
* vpif_buf_cleanup: This function is called from the videobuf2 layer to
|
||||
* free memory allocated to the buffers
|
||||
*/
|
||||
static void vpif_buf_cleanup(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct vpif_disp_buffer *buf = container_of(vb,
|
||||
struct vpif_disp_buffer, vb);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
unsigned long flags;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
spin_lock_irqsave(&common->irqlock, flags);
|
||||
if (vb->state == VB2_BUF_STATE_ACTIVE)
|
||||
list_del_init(&buf->list);
|
||||
spin_unlock_irqrestore(&common->irqlock, flags);
|
||||
}
|
||||
|
||||
static void vpif_wait_prepare(struct vb2_queue *vq)
|
||||
{
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
mutex_unlock(&common->lock);
|
||||
}
|
||||
|
||||
static void vpif_wait_finish(struct vb2_queue *vq)
|
||||
{
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
mutex_lock(&common->lock);
|
||||
}
|
||||
|
||||
static int vpif_buffer_init(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vpif_disp_buffer *buf = container_of(vb,
|
||||
struct vpif_disp_buffer, vb);
|
||||
|
||||
INIT_LIST_HEAD(&buf->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct videobuf_queue_ops video_qops = {
|
||||
.buf_setup = vpif_buffer_setup,
|
||||
.buf_prepare = vpif_buffer_prepare,
|
||||
.buf_queue = vpif_buffer_queue,
|
||||
.buf_release = vpif_buffer_release,
|
||||
};
|
||||
static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
|
||||
|
||||
static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
|
||||
{
|
||||
struct vpif_display_config *vpif_config_data =
|
||||
vpif_dev->platform_data;
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
struct vpif_params *vpif = &ch->vpifparams;
|
||||
unsigned long addr = 0;
|
||||
int ret;
|
||||
|
||||
/* If buffer queue is empty, return error */
|
||||
if (list_empty(&common->dma_queue)) {
|
||||
vpif_err("buffer queue is empty\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get the next frame from the buffer queue */
|
||||
common->next_frm = common->cur_frm =
|
||||
list_entry(common->dma_queue.next,
|
||||
struct vpif_disp_buffer, list);
|
||||
|
||||
list_del(&common->cur_frm->list);
|
||||
/* Mark state of the current frame to active */
|
||||
common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
|
||||
|
||||
/* Initialize field_id and started member */
|
||||
ch->field_id = 0;
|
||||
common->started = 1;
|
||||
addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
|
||||
/* Calculate the offset for Y and C data in the buffer */
|
||||
vpif_calculate_offsets(ch);
|
||||
|
||||
if ((ch->vpifparams.std_info.frm_fmt &&
|
||||
((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
|
||||
&& (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
|
||||
|| (!ch->vpifparams.std_info.frm_fmt
|
||||
&& (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
|
||||
vpif_err("conflict in field format and std format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* clock settings */
|
||||
ret =
|
||||
vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
|
||||
ch->vpifparams.std_info.hd_sd);
|
||||
if (ret < 0) {
|
||||
vpif_err("can't set clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set the parameters and addresses */
|
||||
ret = vpif_set_video_params(vpif, ch->channel_id + 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
common->started = ret;
|
||||
vpif_config_addr(ch, ret);
|
||||
common->set_addr((addr + common->ytop_off),
|
||||
(addr + common->ybtm_off),
|
||||
(addr + common->ctop_off),
|
||||
(addr + common->cbtm_off));
|
||||
|
||||
/* Set interrupt for both the fields in VPIF
|
||||
Register enable channel in VPIF register */
|
||||
if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
|
||||
channel2_intr_assert();
|
||||
channel2_intr_enable(1);
|
||||
enable_channel2(1);
|
||||
if (vpif_config_data->ch2_clip_en)
|
||||
channel2_clipping_enable(1);
|
||||
}
|
||||
|
||||
if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
|
||||
|| (common->started == 2)) {
|
||||
channel3_intr_assert();
|
||||
channel3_intr_enable(1);
|
||||
enable_channel3(1);
|
||||
if (vpif_config_data->ch3_clip_en)
|
||||
channel3_clipping_enable(1);
|
||||
}
|
||||
channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* abort streaming and wait for last buffer */
|
||||
static int vpif_stop_streaming(struct vb2_queue *vq)
|
||||
{
|
||||
struct vpif_fh *fh = vb2_get_drv_priv(vq);
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
|
||||
if (!vb2_is_streaming(vq))
|
||||
return 0;
|
||||
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
/* release all active buffers */
|
||||
while (!list_empty(&common->dma_queue)) {
|
||||
common->next_frm = list_entry(common->dma_queue.next,
|
||||
struct vpif_disp_buffer, list);
|
||||
list_del(&common->next_frm->list);
|
||||
vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vb2_ops video_qops = {
|
||||
.queue_setup = vpif_buffer_queue_setup,
|
||||
.wait_prepare = vpif_wait_prepare,
|
||||
.wait_finish = vpif_wait_finish,
|
||||
.buf_init = vpif_buffer_init,
|
||||
.buf_prepare = vpif_buffer_prepare,
|
||||
.start_streaming = vpif_start_streaming,
|
||||
.stop_streaming = vpif_stop_streaming,
|
||||
.buf_cleanup = vpif_buf_cleanup,
|
||||
.buf_queue = vpif_buffer_queue,
|
||||
};
|
||||
|
||||
static void process_progressive_mode(struct common_obj *common)
|
||||
{
|
||||
unsigned long addr = 0;
|
||||
|
||||
/* Get the next buffer from buffer queue */
|
||||
common->next_frm = list_entry(common->dma_queue.next,
|
||||
struct videobuf_buffer, queue);
|
||||
struct vpif_disp_buffer, list);
|
||||
/* Remove that buffer from the buffer queue */
|
||||
list_del(&common->next_frm->queue);
|
||||
list_del(&common->next_frm->list);
|
||||
/* Mark status of the buffer as active */
|
||||
common->next_frm->state = VIDEOBUF_ACTIVE;
|
||||
common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
|
||||
|
||||
/* Set top and bottom field addrs in VPIF registers */
|
||||
addr = videobuf_to_dma_contig(common->next_frm);
|
||||
addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
|
||||
common->set_addr(addr + common->ytop_off,
|
||||
addr + common->ybtm_off,
|
||||
addr + common->ctop_off,
|
||||
|
@ -271,11 +389,10 @@ 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->ts);
|
||||
do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
|
||||
/* Change status of the cur_frm */
|
||||
common->cur_frm->state = VIDEOBUF_DONE;
|
||||
/* unlock semaphore on cur_frm */
|
||||
wake_up_interruptible(&common->cur_frm->done);
|
||||
vb2_buffer_done(&common->cur_frm->vb,
|
||||
VB2_BUF_STATE_DONE);
|
||||
/* Make cur_frm pointing to next_frm */
|
||||
common->cur_frm = common->next_frm;
|
||||
|
||||
|
@ -307,6 +424,9 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
|
|||
int channel_id = 0;
|
||||
|
||||
channel_id = *(int *)(dev_id);
|
||||
if (!vpif_intr_status(channel_id + 2))
|
||||
return IRQ_NONE;
|
||||
|
||||
ch = dev->dev[channel_id];
|
||||
field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
|
||||
for (i = 0; i < VPIF_NUMOBJECTS; i++) {
|
||||
|
@ -323,9 +443,10 @@ 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->ts);
|
||||
common->cur_frm->state = VIDEOBUF_DONE;
|
||||
wake_up_interruptible(&common->cur_frm->done);
|
||||
do_gettimeofday(&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 */
|
||||
common->cur_frm = common->next_frm;
|
||||
}
|
||||
|
@ -443,10 +564,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
|
|||
vid_ch->buf_field = common->fmt.fmt.pix.field;
|
||||
}
|
||||
|
||||
if (V4L2_MEMORY_USERPTR == common->memory)
|
||||
sizeimage = common->fmt.fmt.pix.sizeimage;
|
||||
else
|
||||
sizeimage = config_params.channel_bufsize[ch->channel_id];
|
||||
sizeimage = common->fmt.fmt.pix.sizeimage;
|
||||
|
||||
hpitch = common->fmt.fmt.pix.bytesperline;
|
||||
vpitch = sizeimage / (hpitch * 2);
|
||||
|
@ -523,10 +641,7 @@ static int vpif_check_format(struct channel_obj *ch,
|
|||
if (pixfmt->bytesperline <= 0)
|
||||
goto invalid_pitch_exit;
|
||||
|
||||
if (V4L2_MEMORY_USERPTR == common->memory)
|
||||
sizeimage = pixfmt->sizeimage;
|
||||
else
|
||||
sizeimage = config_params.channel_bufsize[ch->channel_id];
|
||||
sizeimage = pixfmt->sizeimage;
|
||||
|
||||
if (vpif_update_resolution(ch))
|
||||
return -EINVAL;
|
||||
|
@ -583,7 +698,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
|
|||
|
||||
vpif_dbg(2, debug, "vpif_mmap\n");
|
||||
|
||||
return videobuf_mmap_mapper(&common->buffer_queue, vma);
|
||||
return vb2_mmap(&common->buffer_queue, vma);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -596,7 +711,7 @@ static unsigned int vpif_poll(struct file *filep, poll_table *wait)
|
|||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
if (common->started)
|
||||
return videobuf_poll_stream(filep, &common->buffer_queue, wait);
|
||||
return vb2_poll(&common->buffer_queue, filep, wait);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -665,9 +780,11 @@ static int vpif_release(struct file *filep)
|
|||
channel3_intr_enable(0);
|
||||
}
|
||||
common->started = 0;
|
||||
|
||||
/* Free buffers allocated */
|
||||
videobuf_queue_cancel(&common->buffer_queue);
|
||||
videobuf_mmap_free(&common->buffer_queue);
|
||||
vb2_queue_release(&common->buffer_queue);
|
||||
vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
|
||||
|
||||
common->numbuffers =
|
||||
config_params.numbuffers[ch->channel_id];
|
||||
}
|
||||
|
@ -806,6 +923,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
|
|||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common;
|
||||
enum v4l2_field field;
|
||||
struct vb2_queue *q;
|
||||
u8 index = 0;
|
||||
|
||||
/* This file handle has not initialized the channel,
|
||||
|
@ -825,9 +943,8 @@ static int vpif_reqbufs(struct file *file, void *priv,
|
|||
|
||||
common = &ch->common[index];
|
||||
|
||||
if (common->fmt.type != reqbuf->type)
|
||||
if (common->fmt.type != reqbuf->type || !vpif_dev)
|
||||
return -EINVAL;
|
||||
|
||||
if (0 != common->io_usrs)
|
||||
return -EBUSY;
|
||||
|
||||
|
@ -839,14 +956,21 @@ static int vpif_reqbufs(struct file *file, void *priv,
|
|||
} else {
|
||||
field = V4L2_VBI_INTERLACED;
|
||||
}
|
||||
/* Initialize videobuf2 queue as per the buffer type */
|
||||
common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
|
||||
if (!common->alloc_ctx) {
|
||||
vpif_err("Failed to get the context\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
q = &common->buffer_queue;
|
||||
q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
|
||||
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 vpif_disp_buffer);
|
||||
|
||||
/* Initialize videobuf queue as per the buffer type */
|
||||
videobuf_queue_dma_contig_init(&common->buffer_queue,
|
||||
&video_qops, NULL,
|
||||
&common->irqlock,
|
||||
reqbuf->type, field,
|
||||
sizeof(struct videobuf_buffer), fh,
|
||||
&common->lock);
|
||||
vb2_queue_init(q);
|
||||
|
||||
/* Set io allowed member of file handle to TRUE */
|
||||
fh->io_allowed[index] = 1;
|
||||
|
@ -855,9 +979,8 @@ static int vpif_reqbufs(struct file *file, void *priv,
|
|||
/* Store type of memory requested in channel object */
|
||||
common->memory = reqbuf->memory;
|
||||
INIT_LIST_HEAD(&common->dma_queue);
|
||||
|
||||
/* Allocate buffers */
|
||||
return videobuf_reqbufs(&common->buffer_queue, reqbuf);
|
||||
return vb2_reqbufs(&common->buffer_queue, reqbuf);
|
||||
}
|
||||
|
||||
static int vpif_querybuf(struct file *file, void *priv,
|
||||
|
@ -870,22 +993,25 @@ static int vpif_querybuf(struct file *file, void *priv,
|
|||
if (common->fmt.type != tbuf->type)
|
||||
return -EINVAL;
|
||||
|
||||
return videobuf_querybuf(&common->buffer_queue, tbuf);
|
||||
return vb2_querybuf(&common->buffer_queue, tbuf);
|
||||
}
|
||||
|
||||
static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
||||
{
|
||||
struct vpif_fh *fh = NULL;
|
||||
struct channel_obj *ch = NULL;
|
||||
struct common_obj *common = NULL;
|
||||
|
||||
struct vpif_fh *fh = priv;
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
struct v4l2_buffer tbuf = *buf;
|
||||
struct videobuf_buffer *buf1;
|
||||
unsigned long addr = 0;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
if (!buf || !priv)
|
||||
return -EINVAL;
|
||||
|
||||
if (common->fmt.type != tbuf.type)
|
||||
fh = priv;
|
||||
ch = fh->channel;
|
||||
if (!ch)
|
||||
return -EINVAL;
|
||||
|
||||
common = &(ch->common[VPIF_VIDEO_INDEX]);
|
||||
if (common->fmt.type != buf->type)
|
||||
return -EINVAL;
|
||||
|
||||
if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
|
||||
|
@ -893,73 +1019,7 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
|
|||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!(list_empty(&common->dma_queue)) ||
|
||||
(common->cur_frm != common->next_frm) ||
|
||||
!(common->started) ||
|
||||
(common->started && (0 == ch->field_id)))
|
||||
return videobuf_qbuf(&common->buffer_queue, buf);
|
||||
|
||||
/* bufferqueue is empty store buffer address in VPIF registers */
|
||||
mutex_lock(&common->buffer_queue.vb_lock);
|
||||
buf1 = common->buffer_queue.bufs[tbuf.index];
|
||||
if (buf1->memory != tbuf.memory) {
|
||||
vpif_err("invalid buffer type\n");
|
||||
goto qbuf_exit;
|
||||
}
|
||||
|
||||
if ((buf1->state == VIDEOBUF_QUEUED) ||
|
||||
(buf1->state == VIDEOBUF_ACTIVE)) {
|
||||
vpif_err("invalid state\n");
|
||||
goto qbuf_exit;
|
||||
}
|
||||
|
||||
switch (buf1->memory) {
|
||||
case V4L2_MEMORY_MMAP:
|
||||
if (buf1->baddr == 0)
|
||||
goto qbuf_exit;
|
||||
break;
|
||||
|
||||
case V4L2_MEMORY_USERPTR:
|
||||
if (tbuf.length < buf1->bsize)
|
||||
goto qbuf_exit;
|
||||
|
||||
if ((VIDEOBUF_NEEDS_INIT != buf1->state)
|
||||
&& (buf1->baddr != tbuf.m.userptr)) {
|
||||
vpif_buffer_release(&common->buffer_queue, buf1);
|
||||
buf1->baddr = tbuf.m.userptr;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
goto qbuf_exit;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
|
||||
common->buffer_queue.field);
|
||||
if (ret < 0) {
|
||||
local_irq_restore(flags);
|
||||
goto qbuf_exit;
|
||||
}
|
||||
|
||||
buf1->state = VIDEOBUF_ACTIVE;
|
||||
addr = buf1->boff;
|
||||
common->next_frm = buf1;
|
||||
if (tbuf.type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
|
||||
common->set_addr((addr + common->ytop_off),
|
||||
(addr + common->ybtm_off),
|
||||
(addr + common->ctop_off),
|
||||
(addr + common->cbtm_off));
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
list_add_tail(&buf1->stream, &common->buffer_queue.stream);
|
||||
mutex_unlock(&common->buffer_queue.vb_lock);
|
||||
return 0;
|
||||
|
||||
qbuf_exit:
|
||||
mutex_unlock(&common->buffer_queue.vb_lock);
|
||||
return -EINVAL;
|
||||
return vb2_qbuf(&common->buffer_queue, buf);
|
||||
}
|
||||
|
||||
static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
|
||||
|
@ -969,7 +1029,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
|
|||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
int ret = 0;
|
||||
|
||||
if (!(*std_id & DM646X_V4L2_STD))
|
||||
if (!(*std_id & VPIF_V4L2_STD))
|
||||
return -EINVAL;
|
||||
|
||||
if (common->started) {
|
||||
|
@ -1026,7 +1086,7 @@ static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
|
|||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
|
||||
return videobuf_dqbuf(&common->buffer_queue, p,
|
||||
return vb2_dqbuf(&common->buffer_queue, p,
|
||||
(file->f_flags & O_NONBLOCK));
|
||||
}
|
||||
|
||||
|
@ -1037,10 +1097,6 @@ static int vpif_streamon(struct file *file, void *priv,
|
|||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
|
||||
struct vpif_params *vpif = &ch->vpifparams;
|
||||
struct vpif_display_config *vpif_config_data =
|
||||
vpif_dev->platform_data;
|
||||
unsigned long addr = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
|
||||
|
@ -1072,82 +1128,13 @@ static int vpif_streamon(struct file *file, void *priv,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Call videobuf_streamon to start streaming in videobuf */
|
||||
ret = videobuf_streamon(&common->buffer_queue);
|
||||
/* Call vb2_streamon to start streaming in videobuf2 */
|
||||
ret = vb2_streamon(&common->buffer_queue, buftype);
|
||||
if (ret < 0) {
|
||||
vpif_err("videobuf_streamon\n");
|
||||
vpif_err("vb2_streamon\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If buffer queue is empty, return error */
|
||||
if (list_empty(&common->dma_queue)) {
|
||||
vpif_err("buffer queue is empty\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get the next frame from the buffer queue */
|
||||
common->next_frm = common->cur_frm =
|
||||
list_entry(common->dma_queue.next,
|
||||
struct videobuf_buffer, queue);
|
||||
|
||||
list_del(&common->cur_frm->queue);
|
||||
/* Mark state of the current frame to active */
|
||||
common->cur_frm->state = VIDEOBUF_ACTIVE;
|
||||
|
||||
/* Initialize field_id and started member */
|
||||
ch->field_id = 0;
|
||||
common->started = 1;
|
||||
if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
|
||||
addr = common->cur_frm->boff;
|
||||
/* Calculate the offset for Y and C data in the buffer */
|
||||
vpif_calculate_offsets(ch);
|
||||
|
||||
if ((ch->vpifparams.std_info.frm_fmt &&
|
||||
((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
|
||||
&& (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
|
||||
|| (!ch->vpifparams.std_info.frm_fmt
|
||||
&& (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
|
||||
vpif_err("conflict in field format and std format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* clock settings */
|
||||
ret =
|
||||
vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
|
||||
ch->vpifparams.std_info.hd_sd);
|
||||
if (ret < 0) {
|
||||
vpif_err("can't set clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set the parameters and addresses */
|
||||
ret = vpif_set_video_params(vpif, ch->channel_id + 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
common->started = ret;
|
||||
vpif_config_addr(ch, ret);
|
||||
common->set_addr((addr + common->ytop_off),
|
||||
(addr + common->ybtm_off),
|
||||
(addr + common->ctop_off),
|
||||
(addr + common->cbtm_off));
|
||||
|
||||
/* Set interrupt for both the fields in VPIF
|
||||
Register enable channel in VPIF register */
|
||||
if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
|
||||
channel2_intr_assert();
|
||||
channel2_intr_enable(1);
|
||||
enable_channel2(1);
|
||||
}
|
||||
|
||||
if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
|
||||
|| (common->started == 2)) {
|
||||
channel3_intr_assert();
|
||||
channel3_intr_enable(1);
|
||||
enable_channel3(1);
|
||||
}
|
||||
channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1157,6 +1144,8 @@ static int vpif_streamoff(struct file *file, void *priv,
|
|||
struct vpif_fh *fh = priv;
|
||||
struct channel_obj *ch = fh->channel;
|
||||
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
struct vpif_display_config *vpif_config_data =
|
||||
vpif_dev->platform_data;
|
||||
|
||||
if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
|
||||
vpif_err("buffer type not supported\n");
|
||||
|
@ -1176,18 +1165,22 @@ static int vpif_streamoff(struct file *file, void *priv,
|
|||
if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
|
||||
/* disable channel */
|
||||
if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
|
||||
if (vpif_config_data->ch2_clip_en)
|
||||
channel2_clipping_enable(0);
|
||||
enable_channel2(0);
|
||||
channel2_intr_enable(0);
|
||||
}
|
||||
if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
|
||||
(2 == common->started)) {
|
||||
if (vpif_config_data->ch3_clip_en)
|
||||
channel3_clipping_enable(0);
|
||||
enable_channel3(0);
|
||||
channel3_intr_enable(0);
|
||||
}
|
||||
}
|
||||
|
||||
common->started = 0;
|
||||
return videobuf_streamoff(&common->buffer_queue);
|
||||
return vb2_streamoff(&common->buffer_queue, buftype);
|
||||
}
|
||||
|
||||
static int vpif_cropcap(struct file *file, void *priv,
|
||||
|
@ -1220,7 +1213,7 @@ static int vpif_enum_output(struct file *file, void *fh,
|
|||
|
||||
strcpy(output->name, config->output[output->index]);
|
||||
output->type = V4L2_OUTPUT_TYPE_ANALOG;
|
||||
output->std = DM646X_V4L2_STD;
|
||||
output->std = VPIF_V4L2_STD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1605,7 +1598,7 @@ static struct video_device vpif_video_template = {
|
|||
.name = "vpif",
|
||||
.fops = &vpif_fops,
|
||||
.ioctl_ops = &vpif_ioctl_ops,
|
||||
.tvnorms = DM646X_V4L2_STD,
|
||||
.tvnorms = VPIF_V4L2_STD,
|
||||
.current_norm = V4L2_STD_625_50,
|
||||
|
||||
};
|
||||
|
@ -1687,9 +1680,9 @@ static __init int vpif_probe(struct platform_device *pdev)
|
|||
struct video_device *vfd;
|
||||
struct resource *res;
|
||||
int subdev_count;
|
||||
size_t size;
|
||||
|
||||
vpif_dev = &pdev->dev;
|
||||
|
||||
err = initialize_vpif();
|
||||
|
||||
if (err) {
|
||||
|
@ -1706,8 +1699,8 @@ static __init int vpif_probe(struct platform_device *pdev)
|
|||
k = 0;
|
||||
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
|
||||
for (i = res->start; i <= res->end; i++) {
|
||||
if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
|
||||
"DM646x_Display",
|
||||
if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
|
||||
"VPIF_Display",
|
||||
(void *)(&vpif_obj.dev[k]->channel_id))) {
|
||||
err = -EBUSY;
|
||||
goto vpif_int_err;
|
||||
|
@ -1737,13 +1730,31 @@ static __init int vpif_probe(struct platform_device *pdev)
|
|||
vfd->v4l2_dev = &vpif_obj.v4l2_dev;
|
||||
vfd->release = video_device_release;
|
||||
snprintf(vfd->name, sizeof(vfd->name),
|
||||
"DM646x_VPIFDisplay_DRIVER_V%s",
|
||||
"VPIF_Display_DRIVER_V%s",
|
||||
VPIF_DISPLAY_VERSION);
|
||||
|
||||
/* Set video_dev to the video device */
|
||||
ch->video_dev = vfd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res) {
|
||||
size = resource_size(res);
|
||||
/* The resources are divided into two equal memory and when
|
||||
* we have HD output we can add them together
|
||||
*/
|
||||
for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
|
||||
ch = vpif_obj.dev[j];
|
||||
ch->channel_id = j;
|
||||
|
||||
/* only enabled if second resource exists */
|
||||
config_params.video_limit[ch->channel_id] = 0;
|
||||
if (size)
|
||||
config_params.video_limit[ch->channel_id] =
|
||||
size/2;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
|
||||
ch = vpif_obj.dev[j];
|
||||
/* Initialize field of the channel objects */
|
||||
|
@ -1823,7 +1834,7 @@ static __init int vpif_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
v4l2_info(&vpif_obj.v4l2_dev,
|
||||
"DM646x VPIF display driver initialized\n");
|
||||
" VPIF display driver initialized\n");
|
||||
return 0;
|
||||
|
||||
probe_subdev_out:
|
||||
|
@ -1871,10 +1882,81 @@ static int vpif_remove(struct platform_device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int vpif_suspend(struct device *dev)
|
||||
{
|
||||
struct common_obj *common;
|
||||
struct channel_obj *ch;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
|
||||
/* Get the pointer to the channel object */
|
||||
ch = vpif_obj.dev[i];
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
mutex_lock(&common->lock);
|
||||
if (atomic_read(&ch->usrs) && common->io_usrs) {
|
||||
/* Disable channel */
|
||||
if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
|
||||
enable_channel2(0);
|
||||
channel2_intr_enable(0);
|
||||
}
|
||||
if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
|
||||
common->started == 2) {
|
||||
enable_channel3(0);
|
||||
channel3_intr_enable(0);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&common->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpif_resume(struct device *dev)
|
||||
{
|
||||
|
||||
struct common_obj *common;
|
||||
struct channel_obj *ch;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
|
||||
/* Get the pointer to the channel object */
|
||||
ch = vpif_obj.dev[i];
|
||||
common = &ch->common[VPIF_VIDEO_INDEX];
|
||||
mutex_lock(&common->lock);
|
||||
if (atomic_read(&ch->usrs) && common->io_usrs) {
|
||||
/* Enable channel */
|
||||
if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
|
||||
enable_channel2(1);
|
||||
channel2_intr_enable(1);
|
||||
}
|
||||
if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
|
||||
common->started == 2) {
|
||||
enable_channel3(1);
|
||||
channel3_intr_enable(1);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&common->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops vpif_pm = {
|
||||
.suspend = vpif_suspend,
|
||||
.resume = vpif_resume,
|
||||
};
|
||||
|
||||
#define vpif_pm_ops (&vpif_pm)
|
||||
#else
|
||||
#define vpif_pm_ops NULL
|
||||
#endif
|
||||
|
||||
static __refdata struct platform_driver vpif_driver = {
|
||||
.driver = {
|
||||
.name = "vpif_display",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = vpif_pm_ops,
|
||||
},
|
||||
.probe = vpif_probe,
|
||||
.remove = vpif_remove,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* DM646x display header file
|
||||
* VPIF display header file
|
||||
*
|
||||
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
|
@ -21,7 +21,7 @@
|
|||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/videobuf-core.h>
|
||||
#include <media/videobuf-dma-contig.h>
|
||||
#include <media/videobuf2-dma-contig.h>
|
||||
#include <media/davinci/vpif_types.h>
|
||||
|
||||
#include "vpif.h"
|
||||
|
@ -73,21 +73,29 @@ struct vbi_obj {
|
|||
* vbi data */
|
||||
};
|
||||
|
||||
struct vpif_disp_buffer {
|
||||
struct vb2_buffer vb;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct common_obj {
|
||||
/* Buffer specific parameters */
|
||||
u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for
|
||||
* storing frames */
|
||||
u32 numbuffers; /* number of buffers */
|
||||
struct videobuf_buffer *cur_frm; /* Pointer pointing to current
|
||||
* videobuf_buffer */
|
||||
struct videobuf_buffer *next_frm; /* Pointer pointing to next
|
||||
* videobuf_buffer */
|
||||
struct vpif_disp_buffer *cur_frm; /* Pointer pointing to current
|
||||
* vb2_buffer */
|
||||
struct vpif_disp_buffer *next_frm; /* Pointer pointing to next
|
||||
* vb2_buffer */
|
||||
enum v4l2_memory memory; /* This field keeps track of
|
||||
* type of buffer exchange
|
||||
* method user has selected */
|
||||
struct v4l2_format fmt; /* Used to store the format */
|
||||
struct videobuf_queue buffer_queue; /* Buffer queue used in
|
||||
struct vb2_queue buffer_queue; /* Buffer queue used in
|
||||
* video-buf */
|
||||
/* allocator-specific contexts for each plane */
|
||||
struct vb2_alloc_ctx *alloc_ctx;
|
||||
|
||||
struct list_head dma_queue; /* Queue of filled frames */
|
||||
spinlock_t irqlock; /* Used in video-buf */
|
||||
|
||||
|
@ -158,6 +166,7 @@ struct vpif_config_params {
|
|||
u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
|
||||
u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
|
||||
u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
|
||||
u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS];
|
||||
u8 min_numbuffers;
|
||||
};
|
||||
|
||||
|
|
|
@ -33,10 +33,6 @@ struct sd {
|
|||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
|
||||
.bytesperline = 320,
|
||||
|
@ -256,8 +252,6 @@ static void sd_isoc_irq(struct urb *urb)
|
|||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.start = sd_start,
|
||||
|
@ -288,6 +282,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -31,74 +31,18 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
|
|||
MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define QUALITY 50
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
unsigned char brightness;
|
||||
unsigned char contrast;
|
||||
unsigned char colors;
|
||||
u8 quality;
|
||||
#define QUALITY_MIN 30
|
||||
#define QUALITY_MAX 60
|
||||
#define QUALITY_DEF 40
|
||||
struct v4l2_ctrl *brightness;
|
||||
struct v4l2_ctrl *contrast;
|
||||
struct v4l2_ctrl *sat;
|
||||
|
||||
u8 jpeg_hdr[JPEG_HDR_SZ];
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEF 0xd4
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0x0a,
|
||||
.maximum = 0x1f,
|
||||
.step = 1,
|
||||
#define CONTRAST_DEF 0x0c
|
||||
.default_value = CONTRAST_DEF,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Color",
|
||||
.minimum = 0,
|
||||
.maximum = 7,
|
||||
.step = 1,
|
||||
#define COLOR_DEF 3
|
||||
.default_value = COLOR_DEF,
|
||||
},
|
||||
.set = sd_setcolors,
|
||||
.get = sd_getcolors,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
|
||||
.bytesperline = 176,
|
||||
|
@ -817,17 +761,11 @@ static void cx11646_init1(struct gspca_dev *gspca_dev)
|
|||
static int sd_config(struct gspca_dev *gspca_dev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct cam *cam;
|
||||
|
||||
cam = &gspca_dev->cam;
|
||||
cam->cam_mode = vga_mode;
|
||||
cam->nmodes = ARRAY_SIZE(vga_mode);
|
||||
|
||||
sd->brightness = BRIGHTNESS_DEF;
|
||||
sd->contrast = CONTRAST_DEF;
|
||||
sd->colors = COLOR_DEF;
|
||||
sd->quality = QUALITY_DEF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -849,7 +787,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
/* create the JPEG header */
|
||||
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
|
||||
0x22); /* JPEG 411 */
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
jpeg_set_qual(sd->jpeg_hdr, QUALITY);
|
||||
|
||||
cx11646_initsize(gspca_dev);
|
||||
cx11646_fw(gspca_dev);
|
||||
|
@ -903,142 +841,99 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val, s32 sat)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
__u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
|
||||
__u8 reg51c[2];
|
||||
__u8 bright;
|
||||
__u8 colors;
|
||||
|
||||
bright = sd->brightness;
|
||||
regE5cbx[2] = bright;
|
||||
regE5cbx[2] = val;
|
||||
reg_w(gspca_dev, 0x00e5, regE5cbx, 8);
|
||||
reg_r(gspca_dev, 0x00e8, 8);
|
||||
reg_w(gspca_dev, 0x00e5, regE5c, 4);
|
||||
reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
|
||||
|
||||
colors = sd->colors;
|
||||
reg51c[0] = 0x77;
|
||||
reg51c[1] = colors;
|
||||
reg51c[1] = sat;
|
||||
reg_w(gspca_dev, 0x0051, reg51c, 2);
|
||||
reg_w(gspca_dev, 0x0010, reg10, 2);
|
||||
reg_w_val(gspca_dev, 0x0070, reg70);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val, s32 sat)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
__u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; /* seem MSB */
|
||||
/* __u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01}; * LSB */
|
||||
__u8 reg51c[2];
|
||||
|
||||
regE5acx[2] = sd->contrast;
|
||||
regE5acx[2] = val;
|
||||
reg_w(gspca_dev, 0x00e5, regE5acx, 4);
|
||||
reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
|
||||
reg51c[0] = 0x77;
|
||||
reg51c[1] = sd->colors;
|
||||
reg51c[1] = sat;
|
||||
reg_w(gspca_dev, 0x0051, reg51c, 2);
|
||||
reg_w(gspca_dev, 0x0010, reg10, 2);
|
||||
reg_w_val(gspca_dev, 0x0070, reg70);
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->colors = val;
|
||||
if (gspca_dev->streaming) {
|
||||
setbrightness(gspca_dev);
|
||||
setcontrast(gspca_dev);
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val, sd->sat->cur.val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val, sd->sat->cur.val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setbrightness(gspca_dev, sd->brightness->cur.val, ctrl->val);
|
||||
setcontrast(gspca_dev, sd->contrast->cur.val, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
*val = sd->colors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (jcomp->quality < QUALITY_MIN)
|
||||
sd->quality = QUALITY_MIN;
|
||||
else if (jcomp->quality > QUALITY_MAX)
|
||||
sd->quality = QUALITY_MAX;
|
||||
else
|
||||
sd->quality = jcomp->quality;
|
||||
if (gspca_dev->streaming)
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
memset(jcomp, 0, sizeof *jcomp);
|
||||
jcomp->quality = sd->quality;
|
||||
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
||||
| V4L2_JPEG_MARKER_DQT;
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 3);
|
||||
sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 0xd4);
|
||||
sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0x0a, 0x1f, 1, 0x0c);
|
||||
sd->sat = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 7, 1, 3);
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stop0 = sd_stop0,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.get_jcomp = sd_get_jcomp,
|
||||
.set_jcomp = sd_set_jcomp,
|
||||
};
|
||||
|
||||
/* -- module initialisation -- */
|
||||
|
@ -1064,6 +959,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -225,6 +225,15 @@ MODULE_LICENSE("GPL");
|
|||
#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
|
||||
sd->params.version.firmwareRevision == (y))
|
||||
|
||||
#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
|
||||
#define BRIGHTNESS_DEF 50
|
||||
#define CONTRAST_DEF 48
|
||||
#define SATURATION_DEF 50
|
||||
#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
|
||||
#define ILLUMINATORS_1_DEF 0
|
||||
#define ILLUMINATORS_2_DEF 0
|
||||
#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
|
||||
|
||||
/* Developer's Guide Table 5 p 3-34
|
||||
* indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
|
||||
static u8 flicker_jumps[2][2][4] =
|
||||
|
@ -360,135 +369,9 @@ struct sd {
|
|||
atomic_t fps;
|
||||
int exposure_count;
|
||||
u8 exposure_status;
|
||||
struct v4l2_ctrl *freq;
|
||||
u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */
|
||||
u8 first_frame;
|
||||
u8 freq;
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
#define BRIGHTNESS_IDX 0
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 100,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEF 50
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
#define CONTRAST_IDX 1
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 96,
|
||||
.step = 8,
|
||||
#define CONTRAST_DEF 48
|
||||
.default_value = CONTRAST_DEF,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
#define SATURATION_IDX 2
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
.maximum = 100,
|
||||
.step = 1,
|
||||
#define SATURATION_DEF 50
|
||||
.default_value = SATURATION_DEF,
|
||||
},
|
||||
.set = sd_setsaturation,
|
||||
.get = sd_getsaturation,
|
||||
},
|
||||
#define POWER_LINE_FREQUENCY_IDX 3
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Light frequency filter",
|
||||
.minimum = 0,
|
||||
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
|
||||
.step = 1,
|
||||
#define FREQ_DEF 1
|
||||
.default_value = FREQ_DEF,
|
||||
},
|
||||
.set = sd_setfreq,
|
||||
.get = sd_getfreq,
|
||||
},
|
||||
#define ILLUMINATORS_1_IDX 4
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_ILLUMINATORS_1,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Illuminator 1",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
#define ILLUMINATORS_1_DEF 0
|
||||
.default_value = ILLUMINATORS_1_DEF,
|
||||
},
|
||||
.set = sd_setilluminator1,
|
||||
.get = sd_getilluminator1,
|
||||
},
|
||||
#define ILLUMINATORS_2_IDX 5
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_ILLUMINATORS_2,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Illuminator 2",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
#define ILLUMINATORS_2_DEF 0
|
||||
.default_value = ILLUMINATORS_2_DEF,
|
||||
},
|
||||
.set = sd_setilluminator2,
|
||||
.get = sd_getilluminator2,
|
||||
},
|
||||
#define COMP_TARGET_IDX 6
|
||||
{
|
||||
{
|
||||
#define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE
|
||||
.id = V4L2_CID_COMP_TARGET,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Compression Target",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
|
||||
.default_value = COMP_TARGET_DEF,
|
||||
},
|
||||
.set = sd_setcomptarget,
|
||||
.get = sd_getcomptarget,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format mode[] = {
|
||||
|
@ -770,15 +653,6 @@ static void reset_camera_params(struct gspca_dev *gspca_dev)
|
|||
params->apcor.gain2 = 0x16;
|
||||
params->apcor.gain4 = 0x24;
|
||||
params->apcor.gain8 = 0x34;
|
||||
params->flickerControl.flickerMode = 0;
|
||||
params->flickerControl.disabled = 1;
|
||||
|
||||
params->flickerControl.coarseJump =
|
||||
flicker_jumps[sd->mainsFreq]
|
||||
[params->sensorFps.baserate]
|
||||
[params->sensorFps.divisor];
|
||||
params->flickerControl.allowableOverExposure =
|
||||
find_over_exposure(params->colourParams.brightness);
|
||||
params->vlOffset.gain1 = 20;
|
||||
params->vlOffset.gain2 = 24;
|
||||
params->vlOffset.gain4 = 26;
|
||||
|
@ -798,6 +672,15 @@ static void reset_camera_params(struct gspca_dev *gspca_dev)
|
|||
params->sensorFps.divisor = 1;
|
||||
params->sensorFps.baserate = 1;
|
||||
|
||||
params->flickerControl.flickerMode = 0;
|
||||
params->flickerControl.disabled = 1;
|
||||
params->flickerControl.coarseJump =
|
||||
flicker_jumps[sd->mainsFreq]
|
||||
[params->sensorFps.baserate]
|
||||
[params->sensorFps.divisor];
|
||||
params->flickerControl.allowableOverExposure =
|
||||
find_over_exposure(params->colourParams.brightness);
|
||||
|
||||
params->yuvThreshold.yThreshold = 6; /* From windows driver */
|
||||
params->yuvThreshold.uvThreshold = 6; /* From windows driver */
|
||||
|
||||
|
@ -1110,9 +993,6 @@ static int command_setlights(struct gspca_dev *gspca_dev)
|
|||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int ret, p1, p2;
|
||||
|
||||
if (!sd->params.qx3.qx3_detected)
|
||||
return 0;
|
||||
|
||||
p1 = (sd->params.qx3.bottomlight == 0) << 1;
|
||||
p2 = (sd->params.qx3.toplight == 0) << 3;
|
||||
|
||||
|
@ -1551,8 +1431,10 @@ static void restart_flicker(struct gspca_dev *gspca_dev)
|
|||
static int sd_config(struct gspca_dev *gspca_dev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct cam *cam;
|
||||
|
||||
sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
|
||||
reset_camera_params(gspca_dev);
|
||||
|
||||
PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
|
||||
|
@ -1562,8 +1444,25 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->cam_mode = mode;
|
||||
cam->nmodes = ARRAY_SIZE(mode);
|
||||
|
||||
sd_setfreq(gspca_dev, FREQ_DEF);
|
||||
goto_low_power(gspca_dev);
|
||||
/* Check the firmware version. */
|
||||
sd->params.version.firmwareVersion = 0;
|
||||
get_version_information(gspca_dev);
|
||||
if (sd->params.version.firmwareVersion != 1) {
|
||||
PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
|
||||
sd->params.version.firmwareVersion);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* A bug in firmware 1-02 limits gainMode to 2 */
|
||||
if (sd->params.version.firmwareRevision <= 2 &&
|
||||
sd->params.exposure.gainMode > 2) {
|
||||
sd->params.exposure.gainMode = 2;
|
||||
}
|
||||
|
||||
/* set QX3 detected flag */
|
||||
sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
|
||||
sd->params.pnpID.product == 0x0001);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1602,21 +1501,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
/* Check the firmware version. */
|
||||
sd->params.version.firmwareVersion = 0;
|
||||
get_version_information(gspca_dev);
|
||||
if (sd->params.version.firmwareVersion != 1) {
|
||||
PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
|
||||
sd->params.version.firmwareVersion);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* A bug in firmware 1-02 limits gainMode to 2 */
|
||||
if (sd->params.version.firmwareRevision <= 2 &&
|
||||
sd->params.exposure.gainMode > 2) {
|
||||
sd->params.exposure.gainMode = 2;
|
||||
}
|
||||
|
||||
/* set QX3 detected flag */
|
||||
sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
|
||||
sd->params.pnpID.product == 0x0001);
|
||||
|
||||
/* The fatal error checking should be done after
|
||||
* the camera powers up (developer's guide p 3-38) */
|
||||
|
@ -1785,9 +1669,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
or disable the illuminator controls, if this isn't a QX3 */
|
||||
if (sd->params.qx3.qx3_detected)
|
||||
command_setlights(gspca_dev);
|
||||
else
|
||||
gspca_dev->ctrl_dis |=
|
||||
((1 << ILLUMINATORS_1_IDX) | (1 << ILLUMINATORS_2_IDX));
|
||||
|
||||
sd_stopN(gspca_dev);
|
||||
|
||||
|
@ -1871,235 +1752,123 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
|
|||
do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int ret;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
sd->params.colourParams.brightness = val;
|
||||
sd->params.flickerControl.allowableOverExposure =
|
||||
find_over_exposure(sd->params.colourParams.brightness);
|
||||
if (gspca_dev->streaming) {
|
||||
ret = command_setcolourparams(gspca_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
return command_setflickerctrl(gspca_dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
|
||||
return 0;
|
||||
|
||||
*val = sd->params.colourParams.brightness;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->params.colourParams.contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
return command_setcolourparams(gspca_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->params.colourParams.contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->params.colourParams.saturation = val;
|
||||
if (gspca_dev->streaming)
|
||||
return command_setcolourparams(gspca_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->params.colourParams.saturation;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int on;
|
||||
|
||||
switch (val) {
|
||||
case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
|
||||
on = 0;
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
sd->params.colourParams.brightness = ctrl->val;
|
||||
sd->params.flickerControl.allowableOverExposure =
|
||||
find_over_exposure(sd->params.colourParams.brightness);
|
||||
gspca_dev->usb_err = command_setcolourparams(gspca_dev);
|
||||
if (!gspca_dev->usb_err)
|
||||
gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
|
||||
break;
|
||||
case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
|
||||
on = 1;
|
||||
sd->mainsFreq = 0;
|
||||
case V4L2_CID_CONTRAST:
|
||||
sd->params.colourParams.contrast = ctrl->val;
|
||||
gspca_dev->usb_err = command_setcolourparams(gspca_dev);
|
||||
break;
|
||||
case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
|
||||
on = 1;
|
||||
sd->mainsFreq = 1;
|
||||
case V4L2_CID_SATURATION:
|
||||
sd->params.colourParams.saturation = ctrl->val;
|
||||
gspca_dev->usb_err = command_setcolourparams(gspca_dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sd->freq = val;
|
||||
sd->params.flickerControl.coarseJump =
|
||||
flicker_jumps[sd->mainsFreq]
|
||||
[sd->params.sensorFps.baserate]
|
||||
[sd->params.sensorFps.divisor];
|
||||
|
||||
return set_flicker(gspca_dev, on, gspca_dev->streaming);
|
||||
}
|
||||
|
||||
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->params.compressionTarget.frTargeting = val;
|
||||
if (gspca_dev->streaming)
|
||||
return command_setcompressiontarget(gspca_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->params.compressionTarget.frTargeting;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setilluminator(struct gspca_dev *gspca_dev, __s32 val, int n)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int ret;
|
||||
|
||||
if (!sd->params.qx3.qx3_detected)
|
||||
return -EINVAL;
|
||||
|
||||
switch (n) {
|
||||
case 1:
|
||||
sd->params.qx3.bottomlight = val ? 1 : 0;
|
||||
break;
|
||||
case 2:
|
||||
sd->params.qx3.toplight = val ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = command_setlights(gspca_dev);
|
||||
if (ret && ret != -EINVAL)
|
||||
ret = -EBUSY;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
return sd_setilluminator(gspca_dev, val, 1);
|
||||
}
|
||||
|
||||
static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
return sd_setilluminator(gspca_dev, val, 2);
|
||||
}
|
||||
|
||||
static int sd_getilluminator(struct gspca_dev *gspca_dev, __s32 *val, int n)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (!sd->params.qx3.qx3_detected)
|
||||
return -EINVAL;
|
||||
|
||||
switch (n) {
|
||||
case 1:
|
||||
*val = sd->params.qx3.bottomlight;
|
||||
break;
|
||||
case 2:
|
||||
*val = sd->params.qx3.toplight;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
return sd_getilluminator(gspca_dev, val, 1);
|
||||
}
|
||||
|
||||
static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
return sd_getilluminator(gspca_dev, val, 2);
|
||||
}
|
||||
|
||||
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_querymenu *menu)
|
||||
{
|
||||
switch (menu->id) {
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
switch (menu->index) {
|
||||
case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
|
||||
strcpy((char *) menu->name, "NoFliker");
|
||||
return 0;
|
||||
case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
|
||||
strcpy((char *) menu->name, "50 Hz");
|
||||
return 0;
|
||||
case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
|
||||
strcpy((char *) menu->name, "60 Hz");
|
||||
return 0;
|
||||
}
|
||||
sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
|
||||
sd->params.flickerControl.coarseJump =
|
||||
flicker_jumps[sd->mainsFreq]
|
||||
[sd->params.sensorFps.baserate]
|
||||
[sd->params.sensorFps.divisor];
|
||||
|
||||
gspca_dev->usb_err = set_flicker(gspca_dev,
|
||||
ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
|
||||
gspca_dev->streaming);
|
||||
break;
|
||||
case V4L2_CID_COMP_TARGET:
|
||||
switch (menu->index) {
|
||||
case CPIA_COMPRESSION_TARGET_QUALITY:
|
||||
strcpy((char *) menu->name, "Quality");
|
||||
return 0;
|
||||
case CPIA_COMPRESSION_TARGET_FRAMERATE:
|
||||
strcpy((char *) menu->name, "Framerate");
|
||||
return 0;
|
||||
}
|
||||
case V4L2_CID_ILLUMINATORS_1:
|
||||
sd->params.qx3.bottomlight = ctrl->val;
|
||||
gspca_dev->usb_err = command_setlights(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_ILLUMINATORS_2:
|
||||
sd->params.qx3.toplight = ctrl->val;
|
||||
gspca_dev->usb_err = command_setlights(gspca_dev);
|
||||
break;
|
||||
case CPIA1_CID_COMP_TARGET:
|
||||
sd->params.compressionTarget.frTargeting = ctrl->val;
|
||||
gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
static const char * const comp_target_menu[] = {
|
||||
"Quality",
|
||||
"Framerate",
|
||||
NULL
|
||||
};
|
||||
static const struct v4l2_ctrl_config comp_target = {
|
||||
.ops = &sd_ctrl_ops,
|
||||
.id = CPIA1_CID_COMP_TARGET,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Compression Target",
|
||||
.qmenu = comp_target_menu,
|
||||
.max = 1,
|
||||
.def = COMP_TARGET_DEF,
|
||||
};
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 7);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
|
||||
sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
|
||||
FREQ_DEF);
|
||||
if (sd->params.qx3.qx3_detected) {
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
|
||||
ILLUMINATORS_1_DEF);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
|
||||
ILLUMINATORS_2_DEF);
|
||||
}
|
||||
v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.dq_callback = sd_dq_callback,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.querymenu = sd_querymenu,
|
||||
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
||||
.other_input = 1,
|
||||
#endif
|
||||
|
@ -2129,6 +1898,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -32,9 +32,6 @@ MODULE_LICENSE("GPL");
|
|||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
unsigned char brightness;
|
||||
unsigned char contrast;
|
||||
unsigned char colors;
|
||||
unsigned char autogain;
|
||||
|
||||
char sensor;
|
||||
|
@ -44,76 +41,6 @@ struct sd {
|
|||
#define AG_CNT_START 13
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 1,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEF 63
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define CONTRAST_DEF 127
|
||||
.default_value = CONTRAST_DEF,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
#define COLOR_IDX 2
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Color",
|
||||
.minimum = 0,
|
||||
.maximum = 15,
|
||||
.step = 1,
|
||||
#define COLOR_DEF 7
|
||||
.default_value = COLOR_DEF,
|
||||
},
|
||||
.set = sd_setcolors,
|
||||
.get = sd_getcolors,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
#define AUTOGAIN_DEF 1
|
||||
.default_value = AUTOGAIN_DEF,
|
||||
},
|
||||
.set = sd_setautogain,
|
||||
.get = sd_getautogain,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
|
||||
.bytesperline = 320,
|
||||
|
@ -464,36 +391,31 @@ static void Et_init2(struct gspca_dev *gspca_dev)
|
|||
reg_w_val(gspca_dev, 0x80, 0x20); /* 0x20; */
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int i;
|
||||
__u8 brightness = sd->brightness;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
reg_w_val(gspca_dev, ET_O_RED + i, brightness);
|
||||
reg_w_val(gspca_dev, ET_O_RED + i, val);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
__u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
|
||||
__u8 contrast = sd->contrast;
|
||||
|
||||
memset(RGBG, contrast, sizeof(RGBG) - 2);
|
||||
memset(RGBG, val, sizeof(RGBG) - 2);
|
||||
reg_w(gspca_dev, ET_G_RED, RGBG, 6);
|
||||
}
|
||||
|
||||
static void setcolors(struct gspca_dev *gspca_dev)
|
||||
static void setcolors(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
__u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d };
|
||||
__u8 i2cflags = 0x01;
|
||||
/* __u8 green = 0; */
|
||||
__u8 colors = sd->colors;
|
||||
|
||||
I2cc[3] = colors; /* red */
|
||||
I2cc[0] = 15 - colors; /* blue */
|
||||
I2cc[3] = val; /* red */
|
||||
I2cc[0] = 15 - val; /* blue */
|
||||
/* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */
|
||||
/* I2cc[1] = I2cc[2] = green; */
|
||||
if (sd->sensor == SENSOR_PAS106) {
|
||||
|
@ -504,15 +426,16 @@ static void setcolors(struct gspca_dev *gspca_dev)
|
|||
I2cc[3], I2cc[0], green); */
|
||||
}
|
||||
|
||||
static void getcolors(struct gspca_dev *gspca_dev)
|
||||
static s32 getcolors(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->sensor == SENSOR_PAS106) {
|
||||
/* i2c_r(gspca_dev, PAS106_REG9); * blue */
|
||||
i2c_r(gspca_dev, PAS106_REG9 + 3); /* red */
|
||||
sd->colors = gspca_dev->usb_buf[0] & 0x0f;
|
||||
return gspca_dev->usb_buf[0] & 0x0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setautogain(struct gspca_dev *gspca_dev)
|
||||
|
@ -622,8 +545,7 @@ static void Et_init1(struct gspca_dev *gspca_dev)
|
|||
i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1);
|
||||
/* now set by fifo the whole colors setting */
|
||||
reg_w(gspca_dev, ET_G_RED, GainRGBG, 6);
|
||||
getcolors(gspca_dev);
|
||||
setcolors(gspca_dev);
|
||||
setcolors(gspca_dev, getcolors(gspca_dev));
|
||||
}
|
||||
|
||||
/* this function is called at probe time */
|
||||
|
@ -641,12 +563,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
} else {
|
||||
cam->cam_mode = vga_mode;
|
||||
cam->nmodes = ARRAY_SIZE(vga_mode);
|
||||
gspca_dev->ctrl_dis = (1 << COLOR_IDX);
|
||||
}
|
||||
sd->brightness = BRIGHTNESS_DEF;
|
||||
sd->contrast = CONTRAST_DEF;
|
||||
sd->colors = COLOR_DEF;
|
||||
sd->autogain = AUTOGAIN_DEF;
|
||||
sd->ag_cnt = -1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -780,85 +697,68 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->colors = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcolors(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->colors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->autogain = val;
|
||||
if (gspca_dev->streaming)
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolors(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
sd->autogain = ctrl->val;
|
||||
setautogain(gspca_dev);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
*val = sd->autogain;
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 4);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 1, 127, 1, 63);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 255, 1, 127);
|
||||
if (sd->sensor == SENSOR_PAS106)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 15, 1, 7);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
|
@ -892,6 +792,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -299,6 +299,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -521,6 +521,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -930,6 +930,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
|
|||
goto out;
|
||||
}
|
||||
gspca_dev->streaming = 1;
|
||||
v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
|
||||
|
||||
/* some bulk transfers are started by the subdriver */
|
||||
if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
|
||||
|
@ -1049,12 +1050,6 @@ static int vidioc_g_register(struct file *file, void *priv,
|
|||
{
|
||||
struct gspca_dev *gspca_dev = video_drvdata(file);
|
||||
|
||||
if (!gspca_dev->sd_desc->get_chip_ident)
|
||||
return -ENOTTY;
|
||||
|
||||
if (!gspca_dev->sd_desc->get_register)
|
||||
return -ENOTTY;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
return gspca_dev->sd_desc->get_register(gspca_dev, reg);
|
||||
}
|
||||
|
@ -1064,12 +1059,6 @@ static int vidioc_s_register(struct file *file, void *priv,
|
|||
{
|
||||
struct gspca_dev *gspca_dev = video_drvdata(file);
|
||||
|
||||
if (!gspca_dev->sd_desc->get_chip_ident)
|
||||
return -ENOTTY;
|
||||
|
||||
if (!gspca_dev->sd_desc->set_register)
|
||||
return -ENOTTY;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
return gspca_dev->sd_desc->set_register(gspca_dev, reg);
|
||||
}
|
||||
|
@ -1080,9 +1069,6 @@ static int vidioc_g_chip_ident(struct file *file, void *priv,
|
|||
{
|
||||
struct gspca_dev *gspca_dev = video_drvdata(file);
|
||||
|
||||
if (!gspca_dev->sd_desc->get_chip_ident)
|
||||
return -ENOTTY;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
|
||||
}
|
||||
|
@ -1136,8 +1122,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
|
|||
int mode;
|
||||
|
||||
mode = gspca_dev->curr_mode;
|
||||
memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
|
||||
sizeof fmt->fmt.pix);
|
||||
fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
|
||||
/* some drivers use priv internally, zero it before giving it to
|
||||
userspace */
|
||||
fmt->fmt.pix.priv = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1168,8 +1156,10 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
|
|||
/* else
|
||||
; * no chance, return this mode */
|
||||
}
|
||||
memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
|
||||
sizeof fmt->fmt.pix);
|
||||
fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
|
||||
/* some drivers use priv internally, zero it before giving it to
|
||||
userspace */
|
||||
fmt->fmt.pix.priv = 0;
|
||||
return mode; /* used when s_fmt */
|
||||
}
|
||||
|
||||
|
@ -1284,9 +1274,6 @@ static void gspca_release(struct v4l2_device *v4l2_device)
|
|||
struct gspca_dev *gspca_dev =
|
||||
container_of(v4l2_device, struct gspca_dev, v4l2_dev);
|
||||
|
||||
PDEBUG(D_PROBE, "%s released",
|
||||
video_device_node_name(&gspca_dev->vdev));
|
||||
|
||||
v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
|
||||
v4l2_device_unregister(&gspca_dev->v4l2_dev);
|
||||
kfree(gspca_dev->usb_buf);
|
||||
|
@ -1694,8 +1681,6 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv,
|
|||
{
|
||||
struct gspca_dev *gspca_dev = video_drvdata(file);
|
||||
|
||||
if (!gspca_dev->sd_desc->get_jcomp)
|
||||
return -ENOTTY;
|
||||
gspca_dev->usb_err = 0;
|
||||
return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
|
||||
}
|
||||
|
@ -1705,8 +1690,6 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
|
|||
{
|
||||
struct gspca_dev *gspca_dev = video_drvdata(file);
|
||||
|
||||
if (!gspca_dev->sd_desc->set_jcomp)
|
||||
return -ENOTTY;
|
||||
gspca_dev->usb_err = 0;
|
||||
return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
|
||||
}
|
||||
|
@ -2290,6 +2273,20 @@ int gspca_dev_probe2(struct usb_interface *intf,
|
|||
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
|
||||
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
|
||||
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
|
||||
if (!gspca_dev->sd_desc->get_chip_ident)
|
||||
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_CHIP_IDENT);
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
if (!gspca_dev->sd_desc->get_chip_ident ||
|
||||
!gspca_dev->sd_desc->get_register)
|
||||
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
|
||||
if (!gspca_dev->sd_desc->get_chip_ident ||
|
||||
!gspca_dev->sd_desc->set_register)
|
||||
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER);
|
||||
#endif
|
||||
if (!gspca_dev->sd_desc->get_jcomp)
|
||||
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP);
|
||||
if (!gspca_dev->sd_desc->set_jcomp)
|
||||
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP);
|
||||
|
||||
/* init video stuff */
|
||||
ret = video_register_device(&gspca_dev->vdev,
|
||||
|
@ -2429,7 +2426,6 @@ int gspca_resume(struct usb_interface *intf)
|
|||
*/
|
||||
streaming = gspca_dev->streaming;
|
||||
gspca_dev->streaming = 0;
|
||||
v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
|
||||
if (streaming)
|
||||
ret = gspca_init_transfer(gspca_dev);
|
||||
mutex_unlock(&gspca_dev->usb_lock);
|
||||
|
|
|
@ -54,21 +54,13 @@ enum {
|
|||
#define CAMQUALITY_MIN 0 /* highest cam quality */
|
||||
#define CAMQUALITY_MAX 97 /* lowest cam quality */
|
||||
|
||||
enum e_ctrl {
|
||||
LIGHTFREQ,
|
||||
AUTOGAIN,
|
||||
RED,
|
||||
GREEN,
|
||||
BLUE,
|
||||
NCTRLS /* number of controls */
|
||||
};
|
||||
|
||||
/* Structure to hold all of our device specific stuff */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
struct gspca_ctrl ctrls[NCTRLS];
|
||||
int blocks_left;
|
||||
const struct v4l2_pix_format *cap_mode;
|
||||
struct v4l2_ctrl *freq;
|
||||
struct v4l2_ctrl *jpegqual;
|
||||
/* Driver stuff */
|
||||
u8 type;
|
||||
u8 quality; /* image quality */
|
||||
|
@ -139,23 +131,21 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
|
|||
}
|
||||
}
|
||||
|
||||
static void setfreq(struct gspca_dev *gspca_dev)
|
||||
static void setfreq(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 freq_commands[][2] = {
|
||||
{0x71, 0x80},
|
||||
{0x70, 0x07}
|
||||
};
|
||||
|
||||
freq_commands[0][1] |= (sd->ctrls[LIGHTFREQ].val >> 1);
|
||||
freq_commands[0][1] |= val >> 1;
|
||||
|
||||
jlj_write2(gspca_dev, freq_commands[0]);
|
||||
jlj_write2(gspca_dev, freq_commands[1]);
|
||||
}
|
||||
|
||||
static void setcamquality(struct gspca_dev *gspca_dev)
|
||||
static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 quality_commands[][2] = {
|
||||
{0x71, 0x1E},
|
||||
{0x70, 0x06}
|
||||
|
@ -163,7 +153,7 @@ static void setcamquality(struct gspca_dev *gspca_dev)
|
|||
u8 camquality;
|
||||
|
||||
/* adapt camera quality from jpeg quality */
|
||||
camquality = ((QUALITY_MAX - sd->quality) * CAMQUALITY_MAX)
|
||||
camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX)
|
||||
/ (QUALITY_MAX - QUALITY_MIN);
|
||||
quality_commands[0][1] += camquality;
|
||||
|
||||
|
@ -171,130 +161,58 @@ static void setcamquality(struct gspca_dev *gspca_dev)
|
|||
jlj_write2(gspca_dev, quality_commands[1]);
|
||||
}
|
||||
|
||||
static void setautogain(struct gspca_dev *gspca_dev)
|
||||
static void setautogain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 autogain_commands[][2] = {
|
||||
{0x94, 0x02},
|
||||
{0xcf, 0x00}
|
||||
};
|
||||
|
||||
autogain_commands[1][1] = (sd->ctrls[AUTOGAIN].val << 4);
|
||||
autogain_commands[1][1] = val << 4;
|
||||
|
||||
jlj_write2(gspca_dev, autogain_commands[0]);
|
||||
jlj_write2(gspca_dev, autogain_commands[1]);
|
||||
}
|
||||
|
||||
static void setred(struct gspca_dev *gspca_dev)
|
||||
static void setred(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 setred_commands[][2] = {
|
||||
{0x94, 0x02},
|
||||
{0xe6, 0x00}
|
||||
};
|
||||
|
||||
setred_commands[1][1] = sd->ctrls[RED].val;
|
||||
setred_commands[1][1] = val;
|
||||
|
||||
jlj_write2(gspca_dev, setred_commands[0]);
|
||||
jlj_write2(gspca_dev, setred_commands[1]);
|
||||
}
|
||||
|
||||
static void setgreen(struct gspca_dev *gspca_dev)
|
||||
static void setgreen(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 setgreen_commands[][2] = {
|
||||
{0x94, 0x02},
|
||||
{0xe7, 0x00}
|
||||
};
|
||||
|
||||
setgreen_commands[1][1] = sd->ctrls[GREEN].val;
|
||||
setgreen_commands[1][1] = val;
|
||||
|
||||
jlj_write2(gspca_dev, setgreen_commands[0]);
|
||||
jlj_write2(gspca_dev, setgreen_commands[1]);
|
||||
}
|
||||
|
||||
static void setblue(struct gspca_dev *gspca_dev)
|
||||
static void setblue(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 setblue_commands[][2] = {
|
||||
{0x94, 0x02},
|
||||
{0xe9, 0x00}
|
||||
};
|
||||
|
||||
setblue_commands[1][1] = sd->ctrls[BLUE].val;
|
||||
setblue_commands[1][1] = val;
|
||||
|
||||
jlj_write2(gspca_dev, setblue_commands[0]);
|
||||
jlj_write2(gspca_dev, setblue_commands[1]);
|
||||
}
|
||||
|
||||
static const struct ctrl sd_ctrls[NCTRLS] = {
|
||||
[LIGHTFREQ] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Light frequency filter",
|
||||
.minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 1 */
|
||||
.maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */
|
||||
.step = 1,
|
||||
.default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
|
||||
},
|
||||
.set_control = setfreq
|
||||
},
|
||||
[AUTOGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Automatic Gain (and Exposure)",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
#define AUTOGAIN_DEF 0
|
||||
.default_value = AUTOGAIN_DEF,
|
||||
},
|
||||
.set_control = setautogain
|
||||
},
|
||||
[RED] = {
|
||||
{
|
||||
.id = V4L2_CID_RED_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "red balance",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
#define RED_BALANCE_DEF 2
|
||||
.default_value = RED_BALANCE_DEF,
|
||||
},
|
||||
.set_control = setred
|
||||
},
|
||||
|
||||
[GREEN] = {
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "green balance",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
#define GREEN_BALANCE_DEF 2
|
||||
.default_value = GREEN_BALANCE_DEF,
|
||||
},
|
||||
.set_control = setgreen
|
||||
},
|
||||
[BLUE] = {
|
||||
{
|
||||
.id = V4L2_CID_BLUE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "blue balance",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
#define BLUE_BALANCE_DEF 2
|
||||
.default_value = BLUE_BALANCE_DEF,
|
||||
},
|
||||
.set_control = setblue
|
||||
},
|
||||
};
|
||||
|
||||
static int jlj_start(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
int i;
|
||||
|
@ -344,9 +262,9 @@ static int jlj_start(struct gspca_dev *gspca_dev)
|
|||
if (start_commands[i].ack_wanted)
|
||||
jlj_read1(gspca_dev, response);
|
||||
}
|
||||
setcamquality(gspca_dev);
|
||||
setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
|
||||
msleep(2);
|
||||
setfreq(gspca_dev);
|
||||
setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
|
||||
if (gspca_dev->usb_err < 0)
|
||||
PDEBUG(D_ERR, "Start streaming command failed");
|
||||
return gspca_dev->usb_err;
|
||||
|
@ -403,7 +321,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
struct sd *dev = (struct sd *) gspca_dev;
|
||||
|
||||
dev->type = id->driver_info;
|
||||
gspca_dev->cam.ctrls = dev->ctrls;
|
||||
dev->quality = QUALITY_DEF;
|
||||
|
||||
cam->cam_mode = jlj_mode;
|
||||
|
@ -479,25 +396,81 @@ static const struct usb_device_id device_table[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(usb, device_table);
|
||||
|
||||
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_querymenu *menu)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
switch (menu->id) {
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
switch (menu->index) {
|
||||
case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
|
||||
strcpy((char *) menu->name, "disable");
|
||||
return 0;
|
||||
case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
|
||||
strcpy((char *) menu->name, "50 Hz");
|
||||
return 0;
|
||||
case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
|
||||
strcpy((char *) menu->name, "60 Hz");
|
||||
return 0;
|
||||
}
|
||||
setfreq(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
setred(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
setgreen(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
setblue(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
setautogain(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
|
||||
jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
|
||||
setcamquality(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
static const struct v4l2_ctrl_config custom_autogain = {
|
||||
.ops = &sd_ctrl_ops,
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Automatic Gain (and Exposure)",
|
||||
.max = 3,
|
||||
.step = 1,
|
||||
.def = 0,
|
||||
};
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 6);
|
||||
sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
|
||||
v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 3, 1, 2);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
|
||||
sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_JPEG_COMPRESSION_QUALITY,
|
||||
QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
||||
|
@ -505,16 +478,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
|||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (jcomp->quality < QUALITY_MIN)
|
||||
sd->quality = QUALITY_MIN;
|
||||
else if (jcomp->quality > QUALITY_MAX)
|
||||
sd->quality = QUALITY_MAX;
|
||||
else
|
||||
sd->quality = jcomp->quality;
|
||||
if (gspca_dev->streaming) {
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
setcamquality(gspca_dev);
|
||||
}
|
||||
v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -524,7 +488,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
|||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
memset(jcomp, 0, sizeof *jcomp);
|
||||
jcomp->quality = sd->quality;
|
||||
jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
|
||||
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
||||
| V4L2_JPEG_MARKER_DQT;
|
||||
return 0;
|
||||
|
@ -546,12 +510,10 @@ static const struct sd_desc sd_desc_sportscam_dv15 = {
|
|||
.name = MODULE_NAME,
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.querymenu = sd_querymenu,
|
||||
.get_jcomp = sd_get_jcomp,
|
||||
.set_jcomp = sd_set_jcomp,
|
||||
};
|
||||
|
@ -579,6 +541,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -505,8 +505,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
|
|||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
/* .ctrls = none have been detected */
|
||||
/* .nctrls = ARRAY_SIZE(sd_ctrls), */
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.start = sd_start,
|
||||
|
@ -536,6 +534,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -63,12 +63,6 @@ struct sd {
|
|||
uint8_t ibuf[0x200]; /* input buffer for control commands */
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
/* controls prototypes here */
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
};
|
||||
|
||||
#define MODE_640x480 0x0001
|
||||
#define MODE_640x488 0x0002
|
||||
#define MODE_1280x1024 0x0004
|
||||
|
@ -373,15 +367,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
|
|||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
/*
|
||||
.querymenu = sd_querymenu,
|
||||
.get_streamparm = sd_get_streamparm,
|
||||
.set_streamparm = sd_set_streamparm,
|
||||
*/
|
||||
|
@ -410,6 +401,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -50,107 +50,8 @@ struct sd {
|
|||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
struct urb *last_data_urb;
|
||||
u8 snapshot_pressed;
|
||||
u8 brightness;
|
||||
u8 contrast;
|
||||
u8 saturation;
|
||||
u8 whitebal;
|
||||
u8 sharpness;
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
#define SD_BRIGHTNESS 0
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 9,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEFAULT 4
|
||||
.default_value = BRIGHTNESS_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
#define SD_CONTRAST 1
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 9,
|
||||
.step = 4,
|
||||
#define CONTRAST_DEFAULT 10
|
||||
.default_value = CONTRAST_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
#define SD_SATURATION 2
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
.maximum = 9,
|
||||
.step = 1,
|
||||
#define SATURATION_DEFAULT 4
|
||||
.default_value = SATURATION_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setsaturation,
|
||||
.get = sd_getsaturation,
|
||||
},
|
||||
#define SD_WHITEBAL 3
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "White Balance",
|
||||
.minimum = 0,
|
||||
.maximum = 33,
|
||||
.step = 1,
|
||||
#define WHITEBAL_DEFAULT 25
|
||||
.default_value = WHITEBAL_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setwhitebal,
|
||||
.get = sd_getwhitebal,
|
||||
},
|
||||
#define SD_SHARPNESS 4
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SHARPNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Sharpness",
|
||||
.minimum = 0,
|
||||
.maximum = 9,
|
||||
.step = 1,
|
||||
#define SHARPNESS_DEFAULT 4
|
||||
.default_value = SHARPNESS_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setsharpness,
|
||||
.get = sd_getsharpness,
|
||||
},
|
||||
};
|
||||
|
||||
/* .priv is what goes to register 8 for this mode, known working values:
|
||||
0x00 -> 176x144, cropped
|
||||
|
@ -202,7 +103,8 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
|
|||
0,
|
||||
1000);
|
||||
if (ret < 0) {
|
||||
pr_err("reg_w err %d\n", ret);
|
||||
pr_err("reg_w err writing %02x to %02x: %d\n",
|
||||
value, index, ret);
|
||||
gspca_dev->usb_err = ret;
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +125,7 @@ static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index)
|
|||
2,
|
||||
1000);
|
||||
if (ret < 0) {
|
||||
pr_err("reg_w err %d\n", ret);
|
||||
pr_err("reg_r err %d\n", ret);
|
||||
gspca_dev->usb_err = ret;
|
||||
}
|
||||
}
|
||||
|
@ -242,34 +144,33 @@ static void konica_stream_off(struct gspca_dev *gspca_dev)
|
|||
static int sd_config(struct gspca_dev *gspca_dev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
gspca_dev->cam.cam_mode = vga_mode;
|
||||
gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
|
||||
gspca_dev->cam.no_urb_create = 1;
|
||||
|
||||
sd->brightness = BRIGHTNESS_DEFAULT;
|
||||
sd->contrast = CONTRAST_DEFAULT;
|
||||
sd->saturation = SATURATION_DEFAULT;
|
||||
sd->whitebal = WHITEBAL_DEFAULT;
|
||||
sd->sharpness = SHARPNESS_DEFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this function is called at probe and resume time */
|
||||
static int sd_init(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
/* HDG not sure if these 2 reads are needed */
|
||||
reg_r(gspca_dev, 0, 0x10);
|
||||
PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
|
||||
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
|
||||
reg_r(gspca_dev, 0, 0x10);
|
||||
PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
|
||||
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The konica needs a freaking large time to "boot" (approx 6.5 sec.),
|
||||
* and does not want to be bothered while doing so :|
|
||||
* Register 0x10 counts from 1 - 3, with 3 being "ready"
|
||||
*/
|
||||
msleep(6000);
|
||||
for (i = 0; i < 20; i++) {
|
||||
reg_r(gspca_dev, 0, 0x10);
|
||||
if (gspca_dev->usb_buf[0] == 3)
|
||||
break;
|
||||
msleep(100);
|
||||
}
|
||||
reg_w(gspca_dev, 0, 0x0d);
|
||||
|
||||
return 0;
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_start(struct gspca_dev *gspca_dev)
|
||||
|
@ -289,12 +190,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
|
||||
packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
|
||||
|
||||
reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
|
||||
reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
|
||||
reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
|
||||
reg_w(gspca_dev, sd->saturation, SATURATION_REG);
|
||||
reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
|
||||
|
||||
n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
|
||||
reg_w(gspca_dev, n, 0x08);
|
||||
|
||||
|
@ -479,125 +374,82 @@ resubmit:
|
|||
pr_err("usb_submit_urb(status_urb) ret %d\n", st);
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming) {
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
konica_stream_off(gspca_dev);
|
||||
reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
|
||||
reg_w(gspca_dev, ctrl->val, BRIGHTNESS_REG);
|
||||
konica_stream_on(gspca_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->brightness;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming) {
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
konica_stream_off(gspca_dev);
|
||||
reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
|
||||
reg_w(gspca_dev, ctrl->val, CONTRAST_REG);
|
||||
konica_stream_on(gspca_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->saturation = val;
|
||||
if (gspca_dev->streaming) {
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
konica_stream_off(gspca_dev);
|
||||
reg_w(gspca_dev, sd->saturation, SATURATION_REG);
|
||||
reg_w(gspca_dev, ctrl->val, SATURATION_REG);
|
||||
konica_stream_on(gspca_dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->saturation;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->whitebal = val;
|
||||
if (gspca_dev->streaming) {
|
||||
break;
|
||||
case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
|
||||
konica_stream_off(gspca_dev);
|
||||
reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
|
||||
reg_w(gspca_dev, ctrl->val, WHITEBAL_REG);
|
||||
konica_stream_on(gspca_dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->whitebal;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->sharpness = val;
|
||||
if (gspca_dev->streaming) {
|
||||
break;
|
||||
case V4L2_CID_SHARPNESS:
|
||||
konica_stream_off(gspca_dev);
|
||||
reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
|
||||
reg_w(gspca_dev, ctrl->val, SHARPNESS_REG);
|
||||
konica_stream_on(gspca_dev);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
*val = sd->sharpness;
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 5);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 9, 1, 4);
|
||||
/* Needs to be verified */
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 9, 1, 4);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 9, 1, 4);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_WHITE_BALANCE_TEMPERATURE,
|
||||
0, 33, 1, 25);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SHARPNESS, 0, 9, 1, 4);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
||||
|
@ -628,6 +480,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -400,6 +400,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
.disconnect = m5602_disconnect
|
||||
};
|
||||
|
|
|
@ -30,6 +30,8 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
|
|||
MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define QUALITY 50
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
@ -42,13 +44,6 @@ struct sd {
|
|||
struct v4l2_ctrl *illum_top;
|
||||
struct v4l2_ctrl *illum_bottom;
|
||||
};
|
||||
struct v4l2_ctrl *jpegqual;
|
||||
|
||||
u8 quality;
|
||||
#define QUALITY_MIN 40
|
||||
#define QUALITY_MAX 70
|
||||
#define QUALITY_DEF 50
|
||||
|
||||
u8 jpeg_hdr[JPEG_HDR_SZ];
|
||||
};
|
||||
|
||||
|
@ -194,9 +189,6 @@ static int mars_s_ctrl(struct v4l2_ctrl *ctrl)
|
|||
case V4L2_CID_SHARPNESS:
|
||||
setsharpness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
|
||||
jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -214,7 +206,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
|
|||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 7);
|
||||
v4l2_ctrl_handler_init(hdl, 6);
|
||||
sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 30, 1, 15);
|
||||
sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
|
||||
|
@ -229,9 +221,6 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
|
|||
sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
|
||||
V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
|
||||
sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE;
|
||||
sd->jpegqual = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
|
||||
V4L2_CID_JPEG_COMPRESSION_QUALITY,
|
||||
QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
|
@ -244,13 +233,11 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
|
|||
static int sd_config(struct gspca_dev *gspca_dev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct cam *cam;
|
||||
|
||||
cam = &gspca_dev->cam;
|
||||
cam->cam_mode = vga_mode;
|
||||
cam->nmodes = ARRAY_SIZE(vga_mode);
|
||||
sd->quality = QUALITY_DEF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -269,7 +256,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
/* create the JPEG header */
|
||||
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
|
||||
0x21); /* JPEG 422 */
|
||||
jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
|
||||
jpeg_set_qual(sd->jpeg_hdr, QUALITY);
|
||||
|
||||
data = gspca_dev->usb_buf;
|
||||
|
||||
|
@ -411,31 +398,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
||||
}
|
||||
|
||||
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int ret;
|
||||
|
||||
ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
|
||||
if (ret)
|
||||
return ret;
|
||||
jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
memset(jcomp, 0, sizeof *jcomp);
|
||||
jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
|
||||
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
||||
| V4L2_JPEG_MARKER_DQT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
|
@ -445,8 +407,6 @@ static const struct sd_desc sd_desc = {
|
|||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.get_jcomp = sd_get_jcomp,
|
||||
.set_jcomp = sd_set_jcomp,
|
||||
};
|
||||
|
||||
/* -- module initialisation -- */
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
#define MR97310A_CS_GAIN_MAX 0x7ff
|
||||
#define MR97310A_CS_GAIN_DEFAULT 0x110
|
||||
|
||||
#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000)
|
||||
#define MR97310A_MIN_CLOCKDIV_MIN 3
|
||||
#define MR97310A_MIN_CLOCKDIV_MAX 8
|
||||
#define MR97310A_MIN_CLOCKDIV_DEFAULT 3
|
||||
|
@ -84,17 +85,15 @@ MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
|
|||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
struct { /* exposure/min_clockdiv control cluster */
|
||||
struct v4l2_ctrl *exposure;
|
||||
struct v4l2_ctrl *min_clockdiv;
|
||||
};
|
||||
u8 sof_read;
|
||||
u8 cam_type; /* 0 is CIF and 1 is VGA */
|
||||
u8 sensor_type; /* We use 0 and 1 here, too. */
|
||||
u8 do_lcd_stop;
|
||||
u8 adj_colors;
|
||||
|
||||
int brightness;
|
||||
u16 exposure;
|
||||
u32 gain;
|
||||
u8 contrast;
|
||||
u8 min_clockdiv;
|
||||
};
|
||||
|
||||
struct sensor_w_data {
|
||||
|
@ -105,132 +104,6 @@ struct sensor_w_data {
|
|||
};
|
||||
|
||||
static void sd_stopN(struct gspca_dev *gspca_dev);
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static void setbrightness(struct gspca_dev *gspca_dev);
|
||||
static void setexposure(struct gspca_dev *gspca_dev);
|
||||
static void setgain(struct gspca_dev *gspca_dev);
|
||||
static void setcontrast(struct gspca_dev *gspca_dev);
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
/* Separate brightness control description for Argus QuickClix as it has
|
||||
* different limits from the other mr97310a cameras, and separate gain
|
||||
* control for Sakar CyberPix camera. */
|
||||
{
|
||||
#define NORM_BRIGHTNESS_IDX 0
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = -254,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = MR97310A_BRIGHTNESS_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
{
|
||||
#define ARGUS_QC_BRIGHTNESS_IDX 1
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 15,
|
||||
.step = 1,
|
||||
.default_value = MR97310A_BRIGHTNESS_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
{
|
||||
#define EXPOSURE_IDX 2
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = MR97310A_EXPOSURE_MIN,
|
||||
.maximum = MR97310A_EXPOSURE_MAX,
|
||||
.step = 1,
|
||||
.default_value = MR97310A_EXPOSURE_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setexposure,
|
||||
.get = sd_getexposure,
|
||||
},
|
||||
{
|
||||
#define GAIN_IDX 3
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = MR97310A_GAIN_MIN,
|
||||
.maximum = MR97310A_GAIN_MAX,
|
||||
.step = 1,
|
||||
.default_value = MR97310A_GAIN_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setgain,
|
||||
.get = sd_getgain,
|
||||
},
|
||||
{
|
||||
#define SAKAR_CS_GAIN_IDX 4
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = MR97310A_CS_GAIN_MIN,
|
||||
.maximum = MR97310A_CS_GAIN_MAX,
|
||||
.step = 1,
|
||||
.default_value = MR97310A_CS_GAIN_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setgain,
|
||||
.get = sd_getgain,
|
||||
},
|
||||
{
|
||||
#define CONTRAST_IDX 5
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = MR97310A_CONTRAST_MIN,
|
||||
.maximum = MR97310A_CONTRAST_MAX,
|
||||
.step = 1,
|
||||
.default_value = MR97310A_CONTRAST_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
{
|
||||
#define MIN_CLOCKDIV_IDX 6
|
||||
{
|
||||
.id = V4L2_CID_PRIVATE_BASE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Minimum Clock Divider",
|
||||
.minimum = MR97310A_MIN_CLOCKDIV_MIN,
|
||||
.maximum = MR97310A_MIN_CLOCKDIV_MAX,
|
||||
.step = 1,
|
||||
.default_value = MR97310A_MIN_CLOCKDIV_DEFAULT,
|
||||
.flags = 0,
|
||||
},
|
||||
.set = sd_setmin_clockdiv,
|
||||
.get = sd_getmin_clockdiv,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
|
||||
|
@ -481,7 +354,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct cam *cam;
|
||||
int gain_default = MR97310A_GAIN_DEFAULT;
|
||||
int err_code;
|
||||
|
||||
cam = &gspca_dev->cam;
|
||||
|
@ -615,52 +487,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
sd->sensor_type);
|
||||
}
|
||||
|
||||
/* Setup controls depending on camera type */
|
||||
if (sd->cam_type == CAM_TYPE_CIF) {
|
||||
/* No brightness for sensor_type 0 */
|
||||
if (sd->sensor_type == 0)
|
||||
gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
|
||||
(1 << ARGUS_QC_BRIGHTNESS_IDX) |
|
||||
(1 << CONTRAST_IDX) |
|
||||
(1 << SAKAR_CS_GAIN_IDX);
|
||||
else
|
||||
gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
|
||||
(1 << CONTRAST_IDX) |
|
||||
(1 << SAKAR_CS_GAIN_IDX) |
|
||||
(1 << MIN_CLOCKDIV_IDX);
|
||||
} else {
|
||||
/* All controls need to be disabled if VGA sensor_type is 0 */
|
||||
if (sd->sensor_type == 0)
|
||||
gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
|
||||
(1 << ARGUS_QC_BRIGHTNESS_IDX) |
|
||||
(1 << EXPOSURE_IDX) |
|
||||
(1 << GAIN_IDX) |
|
||||
(1 << CONTRAST_IDX) |
|
||||
(1 << SAKAR_CS_GAIN_IDX) |
|
||||
(1 << MIN_CLOCKDIV_IDX);
|
||||
else if (sd->sensor_type == 2) {
|
||||
gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
|
||||
(1 << ARGUS_QC_BRIGHTNESS_IDX) |
|
||||
(1 << GAIN_IDX) |
|
||||
(1 << MIN_CLOCKDIV_IDX);
|
||||
gain_default = MR97310A_CS_GAIN_DEFAULT;
|
||||
} else if (sd->do_lcd_stop)
|
||||
/* Argus QuickClix has different brightness limits */
|
||||
gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
|
||||
(1 << CONTRAST_IDX) |
|
||||
(1 << SAKAR_CS_GAIN_IDX);
|
||||
else
|
||||
gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
|
||||
(1 << CONTRAST_IDX) |
|
||||
(1 << SAKAR_CS_GAIN_IDX);
|
||||
}
|
||||
|
||||
sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
|
||||
sd->exposure = MR97310A_EXPOSURE_DEFAULT;
|
||||
sd->gain = gain_default;
|
||||
sd->contrast = MR97310A_CONTRAST_DEFAULT;
|
||||
sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -952,11 +778,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
if (err_code < 0)
|
||||
return err_code;
|
||||
|
||||
setbrightness(gspca_dev);
|
||||
setcontrast(gspca_dev);
|
||||
setexposure(gspca_dev);
|
||||
setgain(gspca_dev);
|
||||
|
||||
return isoc_enable(gspca_dev);
|
||||
}
|
||||
|
||||
|
@ -971,37 +792,25 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
|
|||
lcd_stop(gspca_dev);
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */
|
||||
u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
|
||||
static const u8 quick_clix_table[] =
|
||||
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
|
||||
{ 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15};
|
||||
/*
|
||||
* This control is disabled for CIF type 1 and VGA type 0 cameras.
|
||||
* It does not quite act linearly for the Argus QuickClix camera,
|
||||
* but it does control brightness. The values are 0 - 15 only, and
|
||||
* the table above makes them act consecutively.
|
||||
*/
|
||||
if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) &&
|
||||
(gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX)))
|
||||
return;
|
||||
|
||||
if (sd->cam_type == CAM_TYPE_VGA) {
|
||||
sign_reg += 4;
|
||||
value_reg += 4;
|
||||
}
|
||||
|
||||
/* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
|
||||
if (sd->brightness > 0) {
|
||||
if (val > 0) {
|
||||
sensor_write1(gspca_dev, sign_reg, 0x00);
|
||||
val = sd->brightness;
|
||||
} else {
|
||||
sensor_write1(gspca_dev, sign_reg, 0x01);
|
||||
val = (257 - sd->brightness);
|
||||
val = 257 - val;
|
||||
}
|
||||
/* Use lookup table for funky Argus QuickClix brightness */
|
||||
if (sd->do_lcd_stop)
|
||||
|
@ -1010,23 +819,20 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
sensor_write1(gspca_dev, value_reg, val);
|
||||
}
|
||||
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int exposure = MR97310A_EXPOSURE_DEFAULT;
|
||||
u8 buf[2];
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
|
||||
return;
|
||||
|
||||
if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
|
||||
/* This cam does not like exposure settings < 300,
|
||||
so scale 0 - 4095 to 300 - 4095 */
|
||||
exposure = (sd->exposure * 9267) / 10000 + 300;
|
||||
exposure = (expo * 9267) / 10000 + 300;
|
||||
sensor_write1(gspca_dev, 3, exposure >> 4);
|
||||
sensor_write1(gspca_dev, 4, exposure & 0x0f);
|
||||
} else if (sd->sensor_type == 2) {
|
||||
exposure = sd->exposure;
|
||||
exposure = expo;
|
||||
exposure >>= 3;
|
||||
sensor_write1(gspca_dev, 3, exposure >> 8);
|
||||
sensor_write1(gspca_dev, 4, exposure & 0xff);
|
||||
|
@ -1038,11 +844,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
|
||||
Note our 0 - 4095 exposure is mapped to 0 - 511
|
||||
milliseconds exposure time */
|
||||
u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
|
||||
u8 clockdiv = (60 * expo + 7999) / 8000;
|
||||
|
||||
/* Limit framerate to not exceed usb bandwidth */
|
||||
if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
|
||||
clockdiv = sd->min_clockdiv;
|
||||
if (clockdiv < min_clockdiv && gspca_dev->width >= 320)
|
||||
clockdiv = min_clockdiv;
|
||||
else if (clockdiv < 2)
|
||||
clockdiv = 2;
|
||||
|
||||
|
@ -1051,7 +857,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
|
||||
/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
|
||||
exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
|
||||
exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
|
||||
exposure = (60 * 511 * expo) / (8000 * clockdiv);
|
||||
if (exposure > 511)
|
||||
exposure = 511;
|
||||
|
||||
|
@ -1065,125 +871,148 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setgain(struct gspca_dev *gspca_dev)
|
||||
static void setgain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 gainreg;
|
||||
|
||||
if ((gspca_dev->ctrl_dis & (1 << GAIN_IDX)) &&
|
||||
(gspca_dev->ctrl_dis & (1 << SAKAR_CS_GAIN_IDX)))
|
||||
return;
|
||||
|
||||
if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
|
||||
sensor_write1(gspca_dev, 0x0e, sd->gain);
|
||||
sensor_write1(gspca_dev, 0x0e, val);
|
||||
else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
|
||||
for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
|
||||
sensor_write1(gspca_dev, gainreg, sd->gain >> 8);
|
||||
sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff);
|
||||
sensor_write1(gspca_dev, gainreg, val >> 8);
|
||||
sensor_write1(gspca_dev, gainreg + 1, val & 0xff);
|
||||
}
|
||||
else
|
||||
sensor_write1(gspca_dev, 0x10, sd->gain);
|
||||
sensor_write1(gspca_dev, 0x10, val);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
|
||||
return;
|
||||
|
||||
sensor_write1(gspca_dev, 0x1c, sd->contrast);
|
||||
sensor_write1(gspca_dev, 0x1c, val);
|
||||
}
|
||||
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
setexposure(gspca_dev, sd->exposure->val,
|
||||
sd->min_clockdiv ? sd->min_clockdiv->val : 0);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
setgain(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
static const struct v4l2_ctrl_config clockdiv = {
|
||||
.ops = &sd_ctrl_ops,
|
||||
.id = MR97310A_CID_CLOCKDIV,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Minimum Clock Divider",
|
||||
.min = MR97310A_MIN_CLOCKDIV_MIN,
|
||||
.max = MR97310A_MIN_CLOCKDIV_MAX,
|
||||
.step = 1,
|
||||
.def = MR97310A_MIN_CLOCKDIV_DEFAULT,
|
||||
};
|
||||
bool has_brightness = false;
|
||||
bool has_argus_brightness = false;
|
||||
bool has_contrast = false;
|
||||
bool has_gain = false;
|
||||
bool has_cs_gain = false;
|
||||
bool has_exposure = false;
|
||||
bool has_clockdiv = false;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 4);
|
||||
|
||||
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
/* Setup controls depending on camera type */
|
||||
if (sd->cam_type == CAM_TYPE_CIF) {
|
||||
/* No brightness for sensor_type 0 */
|
||||
if (sd->sensor_type == 0)
|
||||
has_exposure = has_gain = has_clockdiv = true;
|
||||
else
|
||||
has_exposure = has_gain = has_brightness = true;
|
||||
} else {
|
||||
/* All controls need to be disabled if VGA sensor_type is 0 */
|
||||
if (sd->sensor_type == 0)
|
||||
; /* no controls! */
|
||||
else if (sd->sensor_type == 2)
|
||||
has_exposure = has_cs_gain = has_contrast = true;
|
||||
else if (sd->do_lcd_stop)
|
||||
has_exposure = has_gain = has_argus_brightness =
|
||||
has_clockdiv = true;
|
||||
else
|
||||
has_exposure = has_gain = has_brightness =
|
||||
has_clockdiv = true;
|
||||
}
|
||||
|
||||
sd->exposure = val;
|
||||
if (gspca_dev->streaming)
|
||||
setexposure(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
/* Separate brightness control description for Argus QuickClix as it has
|
||||
* different limits from the other mr97310a cameras, and separate gain
|
||||
* control for Sakar CyberPix camera. */
|
||||
/*
|
||||
* This control is disabled for CIF type 1 and VGA type 0 cameras.
|
||||
* It does not quite act linearly for the Argus QuickClix camera,
|
||||
* but it does control brightness. The values are 0 - 15 only, and
|
||||
* the table above makes them act consecutively.
|
||||
*/
|
||||
if (has_brightness)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, -254, 255, 1,
|
||||
MR97310A_BRIGHTNESS_DEFAULT);
|
||||
else if (has_argus_brightness)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 15, 1,
|
||||
MR97310A_BRIGHTNESS_DEFAULT);
|
||||
if (has_contrast)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
|
||||
MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT);
|
||||
if (has_gain)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
|
||||
1, MR97310A_GAIN_DEFAULT);
|
||||
else if (has_cs_gain)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
|
||||
MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
|
||||
1, MR97310A_CS_GAIN_DEFAULT);
|
||||
if (has_exposure)
|
||||
sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
|
||||
MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT);
|
||||
if (has_clockdiv)
|
||||
sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
|
||||
|
||||
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->exposure;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->gain = val;
|
||||
if (gspca_dev->streaming)
|
||||
setgain(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->gain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->min_clockdiv = val;
|
||||
if (gspca_dev->streaming)
|
||||
setexposure(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->min_clockdiv;
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
if (has_exposure && has_clockdiv)
|
||||
v4l2_ctrl_cluster(2, &sd->exposure);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1221,10 +1050,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
|
@ -1256,6 +1084,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -32,22 +32,10 @@ MODULE_LICENSE("GPL");
|
|||
|
||||
static int webcam;
|
||||
|
||||
/* controls */
|
||||
enum e_ctrl {
|
||||
GAIN,
|
||||
EXPOSURE,
|
||||
AUTOGAIN,
|
||||
NCTRLS /* number of controls */
|
||||
};
|
||||
|
||||
#define AUTOGAIN_DEF 1
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
struct gspca_ctrl ctrls[NCTRLS];
|
||||
|
||||
u32 ae_res;
|
||||
s8 ag_cnt;
|
||||
#define AG_CNT_START 13
|
||||
|
@ -1667,17 +1655,13 @@ static int swap_bits(int v)
|
|||
return r;
|
||||
}
|
||||
|
||||
static void setgain(struct gspca_dev *gspca_dev)
|
||||
static void setgain(struct gspca_dev *gspca_dev, u8 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val, v[2];
|
||||
u8 v[2];
|
||||
|
||||
val = sd->ctrls[GAIN].val;
|
||||
switch (sd->webcam) {
|
||||
case P35u:
|
||||
/* Note the control goes from 0-255 not 0-127, but anything
|
||||
above 127 just means amplifying noise */
|
||||
val >>= 1; /* 0 - 255 -> 0 - 127 */
|
||||
reg_w(gspca_dev, 0x1026, &val, 1);
|
||||
break;
|
||||
case Kr651us:
|
||||
|
@ -1690,13 +1674,11 @@ static void setgain(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
s16 val;
|
||||
u8 v[2];
|
||||
|
||||
val = sd->ctrls[EXPOSURE].val;
|
||||
switch (sd->webcam) {
|
||||
case P35u:
|
||||
v[0] = ((9 - val) << 3) | 0x01;
|
||||
|
@ -1713,14 +1695,12 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setautogain(struct gspca_dev *gspca_dev)
|
||||
static void setautogain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int w, h;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
|
||||
return;
|
||||
if (!sd->ctrls[AUTOGAIN].val) {
|
||||
if (!val) {
|
||||
sd->ag_cnt = -1;
|
||||
return;
|
||||
}
|
||||
|
@ -1763,7 +1743,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
if ((unsigned) webcam >= NWEBCAMS)
|
||||
webcam = 0;
|
||||
sd->webcam = webcam;
|
||||
gspca_dev->cam.ctrls = sd->ctrls;
|
||||
gspca_dev->cam.needs_full_bandwidth = 1;
|
||||
sd->ag_cnt = -1;
|
||||
|
||||
|
@ -1834,33 +1813,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
break;
|
||||
}
|
||||
}
|
||||
switch (sd->webcam) {
|
||||
case P35u:
|
||||
/* sd->ctrls[EXPOSURE].max = 9;
|
||||
* sd->ctrls[EXPOSURE].def = 9; */
|
||||
/* coarse expo auto gain function gain minimum, to avoid
|
||||
* a large settings jump the first auto adjustment */
|
||||
sd->ctrls[GAIN].def = 255 / 5 * 2;
|
||||
break;
|
||||
case Cvideopro:
|
||||
case DvcV6:
|
||||
case Kritter:
|
||||
gspca_dev->ctrl_dis = (1 << GAIN) | (1 << AUTOGAIN);
|
||||
/* fall thru */
|
||||
case Kr651us:
|
||||
sd->ctrls[EXPOSURE].max = 315;
|
||||
sd->ctrls[EXPOSURE].def = 150;
|
||||
break;
|
||||
default:
|
||||
gspca_dev->ctrl_dis = (1 << GAIN) | (1 << EXPOSURE)
|
||||
| (1 << AUTOGAIN);
|
||||
break;
|
||||
}
|
||||
|
||||
#if AUTOGAIN_DEF
|
||||
if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN)))
|
||||
gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
|
||||
#endif
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
|
@ -1925,9 +1878,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
break;
|
||||
}
|
||||
|
||||
setgain(gspca_dev);
|
||||
setexposure(gspca_dev);
|
||||
setautogain(gspca_dev);
|
||||
sd->exp_too_high_cnt = 0;
|
||||
sd->exp_too_low_cnt = 0;
|
||||
return gspca_dev->usb_err;
|
||||
|
@ -1987,24 +1937,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->ctrls[AUTOGAIN].val = val;
|
||||
if (val)
|
||||
gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
|
||||
else
|
||||
gspca_dev->ctrl_inac = 0;
|
||||
if (gspca_dev->streaming)
|
||||
setautogain(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
#define WANT_REGULAR_AUTOGAIN
|
||||
#define WANT_COARSE_EXPO_AUTOGAIN
|
||||
#include "autogain_functions.h"
|
||||
|
||||
static void do_autogain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
@ -2024,62 +1956,100 @@ static void do_autogain(struct gspca_dev *gspca_dev)
|
|||
|
||||
switch (sd->webcam) {
|
||||
case P35u:
|
||||
coarse_grained_expo_autogain(gspca_dev, luma, 100, 5);
|
||||
gspca_coarse_grained_expo_autogain(gspca_dev, luma, 100, 5);
|
||||
break;
|
||||
default:
|
||||
auto_gain_n_exposure(gspca_dev, luma, 100, 5, 230, 0);
|
||||
gspca_expo_autogain(gspca_dev, luma, 100, 5, 230, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static const struct ctrl sd_ctrls[NCTRLS] = {
|
||||
[GAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 253,
|
||||
.step = 1,
|
||||
.default_value = 128
|
||||
},
|
||||
.set_control = setgain
|
||||
},
|
||||
[EXPOSURE] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 9,
|
||||
.step = 1,
|
||||
.default_value = 9
|
||||
},
|
||||
.set_control = setexposure
|
||||
},
|
||||
[AUTOGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = AUTOGAIN_DEF,
|
||||
.flags = V4L2_CTRL_FLAG_UPDATE
|
||||
},
|
||||
.set = sd_setautogain
|
||||
},
|
||||
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
/* autogain/gain/exposure control cluster */
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
if (ctrl->is_new)
|
||||
setautogain(gspca_dev, ctrl->val);
|
||||
if (!ctrl->val) {
|
||||
if (gspca_dev->gain->is_new)
|
||||
setgain(gspca_dev, gspca_dev->gain->val);
|
||||
if (gspca_dev->exposure->is_new)
|
||||
setexposure(gspca_dev,
|
||||
gspca_dev->exposure->val);
|
||||
}
|
||||
break;
|
||||
/* Some webcams only have exposure, so handle that separately from the
|
||||
autogain/gain/exposure cluster in the previous case. */
|
||||
case V4L2_CID_EXPOSURE:
|
||||
setexposure(gspca_dev, gspca_dev->exposure->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 3);
|
||||
switch (sd->webcam) {
|
||||
case P35u:
|
||||
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
/* For P35u choose coarse expo auto gain function gain minimum,
|
||||
* to avoid a large settings jump the first auto adjustment */
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 127, 1, 127 / 5 * 2);
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 9, 1, 9);
|
||||
break;
|
||||
case Kr651us:
|
||||
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 253, 1, 128);
|
||||
/* fall through */
|
||||
case Cvideopro:
|
||||
case DvcV6:
|
||||
case Kritter:
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 315, 1, 150);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
if (gspca_dev->autogain)
|
||||
v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
|
@ -2117,6 +2087,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -60,25 +60,20 @@ static int frame_rate;
|
|||
* are getting "Failed to read sensor ID..." */
|
||||
static int i2c_detect_tries = 10;
|
||||
|
||||
/* controls */
|
||||
enum e_ctrl {
|
||||
BRIGHTNESS,
|
||||
CONTRAST,
|
||||
EXPOSURE,
|
||||
COLORS,
|
||||
HFLIP,
|
||||
VFLIP,
|
||||
AUTOBRIGHT,
|
||||
AUTOGAIN,
|
||||
FREQ,
|
||||
NCTRL /* number of controls */
|
||||
};
|
||||
|
||||
/* ov519 device descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
struct gspca_ctrl ctrls[NCTRL];
|
||||
struct v4l2_ctrl *jpegqual;
|
||||
struct v4l2_ctrl *freq;
|
||||
struct { /* h/vflip control cluster */
|
||||
struct v4l2_ctrl *hflip;
|
||||
struct v4l2_ctrl *vflip;
|
||||
};
|
||||
struct { /* autobrightness/brightness control cluster */
|
||||
struct v4l2_ctrl *autobright;
|
||||
struct v4l2_ctrl *brightness;
|
||||
};
|
||||
|
||||
u8 packet_nr;
|
||||
|
||||
|
@ -101,7 +96,6 @@ struct sd {
|
|||
/* Determined by sensor type */
|
||||
u8 sif;
|
||||
|
||||
u8 quality;
|
||||
#define QUALITY_MIN 50
|
||||
#define QUALITY_MAX 70
|
||||
#define QUALITY_DEF 50
|
||||
|
@ -145,209 +139,112 @@ enum sensors {
|
|||
really should move the sensor drivers to v4l2 sub drivers. */
|
||||
#include "w996Xcf.c"
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static void setbrightness(struct gspca_dev *gspca_dev);
|
||||
static void setcontrast(struct gspca_dev *gspca_dev);
|
||||
static void setexposure(struct gspca_dev *gspca_dev);
|
||||
static void setcolors(struct gspca_dev *gspca_dev);
|
||||
static void sethvflip(struct gspca_dev *gspca_dev);
|
||||
static void setautobright(struct gspca_dev *gspca_dev);
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static void setfreq(struct gspca_dev *gspca_dev);
|
||||
static void setfreq_i(struct sd *sd);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
[BRIGHTNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setbrightness,
|
||||
},
|
||||
[CONTRAST] = {
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setcontrast,
|
||||
},
|
||||
[EXPOSURE] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setexposure,
|
||||
},
|
||||
[COLORS] = {
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Color",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setcolors,
|
||||
},
|
||||
/* The flip controls work for sensors ov7660 and ov7670 only */
|
||||
[HFLIP] = {
|
||||
{
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Mirror",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = sethvflip,
|
||||
},
|
||||
[VFLIP] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Vflip",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = sethvflip,
|
||||
},
|
||||
[AUTOBRIGHT] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOBRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
.set_control = setautobright,
|
||||
},
|
||||
[AUTOGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
.flags = V4L2_CTRL_FLAG_UPDATE
|
||||
},
|
||||
.set = sd_setautogain,
|
||||
},
|
||||
[FREQ] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Light frequency filter",
|
||||
.minimum = 0,
|
||||
.maximum = 2, /* 0: no flicker, 1: 50Hz, 2:60Hz, 3: auto */
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = setfreq,
|
||||
},
|
||||
/* table of the disabled controls */
|
||||
struct ctrl_valid {
|
||||
int has_brightness:1;
|
||||
int has_contrast:1;
|
||||
int has_exposure:1;
|
||||
int has_autogain:1;
|
||||
int has_sat:1;
|
||||
int has_hvflip:1;
|
||||
int has_autobright:1;
|
||||
int has_freq:1;
|
||||
};
|
||||
|
||||
/* table of the disabled controls */
|
||||
static const unsigned ctrl_dis[] = {
|
||||
[SEN_OV2610] = ((1 << NCTRL) - 1) /* no control */
|
||||
^ ((1 << EXPOSURE) /* but exposure */
|
||||
| (1 << AUTOGAIN)), /* and autogain */
|
||||
|
||||
[SEN_OV2610AE] = ((1 << NCTRL) - 1) /* no control */
|
||||
^ ((1 << EXPOSURE) /* but exposure */
|
||||
| (1 << AUTOGAIN)), /* and autogain */
|
||||
|
||||
[SEN_OV3610] = (1 << NCTRL) - 1, /* no control */
|
||||
|
||||
[SEN_OV6620] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV6630] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV66308AF] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV7610] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV7620] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV7620AE] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV7640] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << AUTOBRIGHT) |
|
||||
(1 << CONTRAST) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV7648] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << AUTOBRIGHT) |
|
||||
(1 << CONTRAST) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV7660] = (1 << AUTOBRIGHT) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV7670] = (1 << COLORS) |
|
||||
(1 << AUTOBRIGHT) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV76BE] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN),
|
||||
|
||||
[SEN_OV8610] = (1 << HFLIP) |
|
||||
(1 << VFLIP) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << AUTOGAIN) |
|
||||
(1 << FREQ),
|
||||
[SEN_OV9600] = ((1 << NCTRL) - 1) /* no control */
|
||||
^ ((1 << EXPOSURE) /* but exposure */
|
||||
| (1 << AUTOGAIN)), /* and autogain */
|
||||
|
||||
static const struct ctrl_valid valid_controls[] = {
|
||||
[SEN_OV2610] = {
|
||||
.has_exposure = 1,
|
||||
.has_autogain = 1,
|
||||
},
|
||||
[SEN_OV2610AE] = {
|
||||
.has_exposure = 1,
|
||||
.has_autogain = 1,
|
||||
},
|
||||
[SEN_OV3610] = {
|
||||
/* No controls */
|
||||
},
|
||||
[SEN_OV6620] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_autobright = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV6630] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_autobright = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV66308AF] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_autobright = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV7610] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_autobright = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV7620] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_autobright = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV7620AE] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_autobright = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV7640] = {
|
||||
.has_brightness = 1,
|
||||
.has_sat = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV7648] = {
|
||||
.has_brightness = 1,
|
||||
.has_sat = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV7660] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_hvflip = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV7670] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_hvflip = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV76BE] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_autobright = 1,
|
||||
.has_freq = 1,
|
||||
},
|
||||
[SEN_OV8610] = {
|
||||
.has_brightness = 1,
|
||||
.has_contrast = 1,
|
||||
.has_sat = 1,
|
||||
.has_autobright = 1,
|
||||
},
|
||||
[SEN_OV9600] = {
|
||||
.has_exposure = 1,
|
||||
.has_autogain = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format ov519_vga_mode[] = {
|
||||
|
@ -3306,11 +3203,11 @@ static void ov519_set_fr(struct sd *sd)
|
|||
ov518_i2c_w(sd, OV7670_R11_CLKRC, clock);
|
||||
}
|
||||
|
||||
static void setautogain(struct gspca_dev *gspca_dev)
|
||||
static void setautogain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
i2c_w_mask(sd, 0x13, sd->ctrls[AUTOGAIN].val ? 0x05 : 0x00, 0x05);
|
||||
i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05);
|
||||
}
|
||||
|
||||
/* this function is called at probe time */
|
||||
|
@ -3351,8 +3248,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
break;
|
||||
}
|
||||
|
||||
gspca_dev->cam.ctrls = sd->ctrls;
|
||||
sd->quality = QUALITY_DEF;
|
||||
sd->frame_rate = 15;
|
||||
|
||||
return 0;
|
||||
|
@ -3467,8 +3362,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
break;
|
||||
}
|
||||
|
||||
gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
|
||||
|
||||
/* initialize the sensor */
|
||||
switch (sd->sensor) {
|
||||
case SEN_OV2610:
|
||||
|
@ -3494,8 +3387,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
break;
|
||||
case SEN_OV6630:
|
||||
case SEN_OV66308AF:
|
||||
sd->ctrls[CONTRAST].def = 200;
|
||||
/* The default is too low for the ov6630 */
|
||||
write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30));
|
||||
break;
|
||||
default:
|
||||
|
@ -3522,26 +3413,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
sd->gspca_dev.curr_mode = 1; /* 640x480 */
|
||||
ov519_set_mode(sd);
|
||||
ov519_set_fr(sd);
|
||||
sd->ctrls[COLORS].max = 4; /* 0..4 */
|
||||
sd->ctrls[COLORS].val =
|
||||
sd->ctrls[COLORS].def = 2;
|
||||
setcolors(gspca_dev);
|
||||
sd->ctrls[CONTRAST].max = 6; /* 0..6 */
|
||||
sd->ctrls[CONTRAST].val =
|
||||
sd->ctrls[CONTRAST].def = 3;
|
||||
setcontrast(gspca_dev);
|
||||
sd->ctrls[BRIGHTNESS].max = 6; /* 0..6 */
|
||||
sd->ctrls[BRIGHTNESS].val =
|
||||
sd->ctrls[BRIGHTNESS].def = 3;
|
||||
setbrightness(gspca_dev);
|
||||
sd_reset_snapshot(gspca_dev);
|
||||
ov51x_restart(sd);
|
||||
ov51x_stop(sd); /* not in win traces */
|
||||
ov51x_led_control(sd, 0);
|
||||
break;
|
||||
case SEN_OV7670:
|
||||
sd->ctrls[FREQ].max = 3; /* auto */
|
||||
sd->ctrls[FREQ].def = 3;
|
||||
write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670));
|
||||
break;
|
||||
case SEN_OV8610:
|
||||
|
@ -4177,15 +4054,14 @@ static void mode_init_ov_sensor_regs(struct sd *sd)
|
|||
}
|
||||
|
||||
/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */
|
||||
static void sethvflip(struct gspca_dev *gspca_dev)
|
||||
static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->gspca_dev.streaming)
|
||||
reg_w(sd, OV519_R51_RESET1, 0x0f); /* block stream */
|
||||
i2c_w_mask(sd, OV7670_R1E_MVFP,
|
||||
OV7670_MVFP_MIRROR * sd->ctrls[HFLIP].val
|
||||
| OV7670_MVFP_VFLIP * sd->ctrls[VFLIP].val,
|
||||
OV7670_MVFP_MIRROR * hflip | OV7670_MVFP_VFLIP * vflip,
|
||||
OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
|
||||
if (sd->gspca_dev.streaming)
|
||||
reg_w(sd, OV519_R51_RESET1, 0x00); /* restart stream */
|
||||
|
@ -4333,23 +4209,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
|
||||
set_ov_sensor_window(sd);
|
||||
|
||||
if (!(sd->gspca_dev.ctrl_dis & (1 << CONTRAST)))
|
||||
setcontrast(gspca_dev);
|
||||
if (!(sd->gspca_dev.ctrl_dis & (1 << BRIGHTNESS)))
|
||||
setbrightness(gspca_dev);
|
||||
if (!(sd->gspca_dev.ctrl_dis & (1 << EXPOSURE)))
|
||||
setexposure(gspca_dev);
|
||||
if (!(sd->gspca_dev.ctrl_dis & (1 << COLORS)))
|
||||
setcolors(gspca_dev);
|
||||
if (!(sd->gspca_dev.ctrl_dis & ((1 << HFLIP) | (1 << VFLIP))))
|
||||
sethvflip(gspca_dev);
|
||||
if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOBRIGHT)))
|
||||
setautobright(gspca_dev);
|
||||
if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOGAIN)))
|
||||
setautogain(gspca_dev);
|
||||
if (!(sd->gspca_dev.ctrl_dis & (1 << FREQ)))
|
||||
setfreq_i(sd);
|
||||
|
||||
/* Force clear snapshot state in case the snapshot button was
|
||||
pressed while we weren't streaming */
|
||||
sd->snapshot_needs_reset = 1;
|
||||
|
@ -4605,10 +4464,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
|
||||
/* -- management routines -- */
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int val;
|
||||
static const struct ov_i2c_regvals brit_7660[][7] = {
|
||||
{{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90},
|
||||
{0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}},
|
||||
|
@ -4626,7 +4484,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
{0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}}
|
||||
};
|
||||
|
||||
val = sd->ctrls[BRIGHTNESS].val;
|
||||
switch (sd->sensor) {
|
||||
case SEN_OV8610:
|
||||
case SEN_OV7610:
|
||||
|
@ -4640,9 +4497,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
break;
|
||||
case SEN_OV7620:
|
||||
case SEN_OV7620AE:
|
||||
/* 7620 doesn't like manual changes when in auto mode */
|
||||
if (!sd->ctrls[AUTOBRIGHT].val)
|
||||
i2c_w(sd, OV7610_REG_BRT, val);
|
||||
i2c_w(sd, OV7610_REG_BRT, val);
|
||||
break;
|
||||
case SEN_OV7660:
|
||||
write_i2c_regvals(sd, brit_7660[val],
|
||||
|
@ -4656,10 +4511,9 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int val;
|
||||
static const struct ov_i2c_regvals contrast_7660[][31] = {
|
||||
{{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0},
|
||||
{0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30},
|
||||
|
@ -4719,7 +4573,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
|
|||
{0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}},
|
||||
};
|
||||
|
||||
val = sd->ctrls[CONTRAST].val;
|
||||
switch (sd->sensor) {
|
||||
case SEN_OV7610:
|
||||
case SEN_OV6620:
|
||||
|
@ -4760,18 +4613,16 @@ static void setcontrast(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (!sd->ctrls[AUTOGAIN].val)
|
||||
i2c_w(sd, 0x10, sd->ctrls[EXPOSURE].val);
|
||||
i2c_w(sd, 0x10, val);
|
||||
}
|
||||
|
||||
static void setcolors(struct gspca_dev *gspca_dev)
|
||||
static void setcolors(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int val;
|
||||
static const struct ov_i2c_regvals colors_7660[][6] = {
|
||||
{{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a},
|
||||
{0x53, 0x19}, {0x54, 0x23}},
|
||||
|
@ -4785,7 +4636,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
|
|||
{0x53, 0x66}, {0x54, 0x8e}},
|
||||
};
|
||||
|
||||
val = sd->ctrls[COLORS].val;
|
||||
switch (sd->sensor) {
|
||||
case SEN_OV8610:
|
||||
case SEN_OV7610:
|
||||
|
@ -4819,34 +4669,18 @@ static void setcolors(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setautobright(struct gspca_dev *gspca_dev)
|
||||
static void setautobright(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
i2c_w_mask(sd, 0x2d, sd->ctrls[AUTOBRIGHT].val ? 0x10 : 0x00, 0x10);
|
||||
i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10);
|
||||
}
|
||||
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->ctrls[AUTOGAIN].val = val;
|
||||
if (val) {
|
||||
gspca_dev->ctrl_inac |= (1 << EXPOSURE);
|
||||
} else {
|
||||
gspca_dev->ctrl_inac &= ~(1 << EXPOSURE);
|
||||
sd->ctrls[EXPOSURE].val = i2c_r(sd, 0x10);
|
||||
}
|
||||
if (gspca_dev->streaming)
|
||||
setautogain(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static void setfreq_i(struct sd *sd)
|
||||
static void setfreq_i(struct sd *sd, s32 val)
|
||||
{
|
||||
if (sd->sensor == SEN_OV7660
|
||||
|| sd->sensor == SEN_OV7670) {
|
||||
switch (sd->ctrls[FREQ].val) {
|
||||
switch (val) {
|
||||
case 0: /* Banding filter disabled */
|
||||
i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT);
|
||||
break;
|
||||
|
@ -4868,7 +4702,7 @@ static void setfreq_i(struct sd *sd)
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
switch (sd->ctrls[FREQ].val) {
|
||||
switch (val) {
|
||||
case 0: /* Banding filter disabled */
|
||||
i2c_w_mask(sd, 0x2d, 0x00, 0x04);
|
||||
i2c_w_mask(sd, 0x2a, 0x00, 0x80);
|
||||
|
@ -4900,56 +4734,28 @@ static void setfreq_i(struct sd *sd)
|
|||
}
|
||||
}
|
||||
}
|
||||
static void setfreq(struct gspca_dev *gspca_dev)
|
||||
|
||||
static void setfreq(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
setfreq_i(sd);
|
||||
setfreq_i(sd, val);
|
||||
|
||||
/* Ugly but necessary */
|
||||
if (sd->bridge == BRIDGE_W9968CF)
|
||||
w9968cf_set_crop_window(sd);
|
||||
}
|
||||
|
||||
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_querymenu *menu)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
switch (menu->id) {
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
switch (menu->index) {
|
||||
case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
|
||||
strcpy((char *) menu->name, "NoFliker");
|
||||
return 0;
|
||||
case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
|
||||
strcpy((char *) menu->name, "50 Hz");
|
||||
return 0;
|
||||
case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
|
||||
strcpy((char *) menu->name, "60 Hz");
|
||||
return 0;
|
||||
case 3:
|
||||
if (sd->sensor != SEN_OV7670)
|
||||
return -EINVAL;
|
||||
|
||||
strcpy((char *) menu->name, "Automatic");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->bridge != BRIDGE_W9968CF)
|
||||
return -EINVAL;
|
||||
return -ENOTTY;
|
||||
|
||||
memset(jcomp, 0, sizeof *jcomp);
|
||||
jcomp->quality = sd->quality;
|
||||
jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
|
||||
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
|
||||
V4L2_JPEG_MARKER_DRI;
|
||||
return 0;
|
||||
|
@ -4961,38 +4767,161 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
|||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->bridge != BRIDGE_W9968CF)
|
||||
return -EINVAL;
|
||||
return -ENOTTY;
|
||||
|
||||
if (gspca_dev->streaming)
|
||||
return -EBUSY;
|
||||
v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (jcomp->quality < QUALITY_MIN)
|
||||
sd->quality = QUALITY_MIN;
|
||||
else if (jcomp->quality > QUALITY_MAX)
|
||||
sd->quality = QUALITY_MAX;
|
||||
else
|
||||
sd->quality = jcomp->quality;
|
||||
static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
/* Return resulting jcomp params to app */
|
||||
sd_get_jcomp(gspca_dev, jcomp);
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
gspca_dev->exposure->val = i2c_r(sd, 0x10);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
setfreq(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_AUTOBRIGHTNESS:
|
||||
if (ctrl->is_new)
|
||||
setautobright(gspca_dev, ctrl->val);
|
||||
if (!ctrl->val && sd->brightness->is_new)
|
||||
setbrightness(gspca_dev, sd->brightness->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolors(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
if (ctrl->is_new)
|
||||
setautogain(gspca_dev, ctrl->val);
|
||||
if (!ctrl->val && gspca_dev->exposure->is_new)
|
||||
setexposure(gspca_dev, gspca_dev->exposure->val);
|
||||
break;
|
||||
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
|
||||
return -EBUSY; /* Should never happen, as we grab the ctrl */
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.g_volatile_ctrl = sd_g_volatile_ctrl,
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 10);
|
||||
if (valid_controls[sd->sensor].has_brightness)
|
||||
sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0,
|
||||
sd->sensor == SEN_OV7660 ? 6 : 255, 1,
|
||||
sd->sensor == SEN_OV7660 ? 3 : 127);
|
||||
if (valid_controls[sd->sensor].has_contrast) {
|
||||
if (sd->sensor == SEN_OV7660)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 6, 1, 3);
|
||||
else
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 255, 1,
|
||||
(sd->sensor == SEN_OV6630 ||
|
||||
sd->sensor == SEN_OV66308AF) ? 200 : 127);
|
||||
}
|
||||
if (valid_controls[sd->sensor].has_sat)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0,
|
||||
sd->sensor == SEN_OV7660 ? 4 : 255, 1,
|
||||
sd->sensor == SEN_OV7660 ? 2 : 127);
|
||||
if (valid_controls[sd->sensor].has_exposure)
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 255, 1, 127);
|
||||
if (valid_controls[sd->sensor].has_hvflip) {
|
||||
sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
}
|
||||
if (valid_controls[sd->sensor].has_autobright)
|
||||
sd->autobright = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1);
|
||||
if (valid_controls[sd->sensor].has_autogain)
|
||||
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
if (valid_controls[sd->sensor].has_freq) {
|
||||
if (sd->sensor == SEN_OV7670)
|
||||
sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
|
||||
else
|
||||
sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
|
||||
}
|
||||
if (sd->bridge == BRIDGE_W9968CF)
|
||||
sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_JPEG_COMPRESSION_QUALITY,
|
||||
QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
if (gspca_dev->autogain)
|
||||
v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, true);
|
||||
if (sd->autobright)
|
||||
v4l2_ctrl_auto_cluster(2, &sd->autobright, 0, false);
|
||||
if (sd->hflip)
|
||||
v4l2_ctrl_cluster(2, &sd->hflip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.isoc_init = sd_isoc_init,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.stop0 = sd_stop0,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.dq_callback = sd_reset_snapshot,
|
||||
.querymenu = sd_querymenu,
|
||||
.get_jcomp = sd_get_jcomp,
|
||||
.set_jcomp = sd_set_jcomp,
|
||||
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
||||
|
@ -5052,6 +4981,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "gspca.h"
|
||||
|
||||
#include <linux/fixp-arith.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
#define OV534_REG_ADDRESS 0xf1 /* sensor address */
|
||||
#define OV534_REG_SUBADDR 0xf2
|
||||
|
@ -53,29 +54,28 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
|
|||
MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* controls */
|
||||
enum e_ctrl {
|
||||
HUE,
|
||||
SATURATION,
|
||||
BRIGHTNESS,
|
||||
CONTRAST,
|
||||
GAIN,
|
||||
EXPOSURE,
|
||||
AGC,
|
||||
AWB,
|
||||
AEC,
|
||||
SHARPNESS,
|
||||
HFLIP,
|
||||
VFLIP,
|
||||
LIGHTFREQ,
|
||||
NCTRLS /* number of controls */
|
||||
};
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
struct gspca_ctrl ctrls[NCTRLS];
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
struct v4l2_ctrl *hue;
|
||||
struct v4l2_ctrl *saturation;
|
||||
struct v4l2_ctrl *brightness;
|
||||
struct v4l2_ctrl *contrast;
|
||||
struct { /* gain control cluster */
|
||||
struct v4l2_ctrl *autogain;
|
||||
struct v4l2_ctrl *gain;
|
||||
};
|
||||
struct v4l2_ctrl *autowhitebalance;
|
||||
struct { /* exposure control cluster */
|
||||
struct v4l2_ctrl *autoexposure;
|
||||
struct v4l2_ctrl *exposure;
|
||||
};
|
||||
struct v4l2_ctrl *sharpness;
|
||||
struct v4l2_ctrl *hflip;
|
||||
struct v4l2_ctrl *vflip;
|
||||
struct v4l2_ctrl *plfreq;
|
||||
|
||||
__u32 last_pts;
|
||||
u16 last_fid;
|
||||
|
@ -89,181 +89,9 @@ enum sensors {
|
|||
NSENSORS
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static void sethue(struct gspca_dev *gspca_dev);
|
||||
static void setsaturation(struct gspca_dev *gspca_dev);
|
||||
static void setbrightness(struct gspca_dev *gspca_dev);
|
||||
static void setcontrast(struct gspca_dev *gspca_dev);
|
||||
static void setgain(struct gspca_dev *gspca_dev);
|
||||
static void setexposure(struct gspca_dev *gspca_dev);
|
||||
static void setagc(struct gspca_dev *gspca_dev);
|
||||
static void setawb(struct gspca_dev *gspca_dev);
|
||||
static void setaec(struct gspca_dev *gspca_dev);
|
||||
static void setsharpness(struct gspca_dev *gspca_dev);
|
||||
static void sethvflip(struct gspca_dev *gspca_dev);
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev);
|
||||
|
||||
static int sd_start(struct gspca_dev *gspca_dev);
|
||||
static void sd_stopN(struct gspca_dev *gspca_dev);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
[HUE] = {
|
||||
{
|
||||
.id = V4L2_CID_HUE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Hue",
|
||||
.minimum = -90,
|
||||
.maximum = 90,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = sethue
|
||||
},
|
||||
[SATURATION] = {
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 64,
|
||||
},
|
||||
.set_control = setsaturation
|
||||
},
|
||||
[BRIGHTNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = setbrightness
|
||||
},
|
||||
[CONTRAST] = {
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 32,
|
||||
},
|
||||
.set_control = setcontrast
|
||||
},
|
||||
[GAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Main Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 63,
|
||||
.step = 1,
|
||||
.default_value = 20,
|
||||
},
|
||||
.set_control = setgain
|
||||
},
|
||||
[EXPOSURE] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 120,
|
||||
},
|
||||
.set_control = setexposure
|
||||
},
|
||||
[AGC] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
.set_control = setagc
|
||||
},
|
||||
[AWB] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTO_WHITE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto White Balance",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
.set_control = setawb
|
||||
},
|
||||
[AEC] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE_AUTO,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
.set_control = setaec
|
||||
},
|
||||
[SHARPNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_SHARPNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Sharpness",
|
||||
.minimum = 0,
|
||||
.maximum = 63,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = setsharpness
|
||||
},
|
||||
[HFLIP] = {
|
||||
{
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "HFlip",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = sethvflip
|
||||
},
|
||||
[VFLIP] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "VFlip",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = sethvflip
|
||||
},
|
||||
[LIGHTFREQ] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Light Frequency Filter",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = setlightfreq
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format ov772x_mode[] = {
|
||||
{320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
|
||||
|
@ -972,12 +800,10 @@ static void set_frame_rate(struct gspca_dev *gspca_dev)
|
|||
PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
|
||||
}
|
||||
|
||||
static void sethue(struct gspca_dev *gspca_dev)
|
||||
static void sethue(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int val;
|
||||
|
||||
val = sd->ctrls[HUE].val;
|
||||
if (sd->sensor == SENSOR_OV767x) {
|
||||
/* TBD */
|
||||
} else {
|
||||
|
@ -1014,12 +840,10 @@ static void sethue(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setsaturation(struct gspca_dev *gspca_dev)
|
||||
static void setsaturation(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int val;
|
||||
|
||||
val = sd->ctrls[SATURATION].val;
|
||||
if (sd->sensor == SENSOR_OV767x) {
|
||||
int i;
|
||||
static u8 color_tb[][6] = {
|
||||
|
@ -1040,12 +864,10 @@ static void setsaturation(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int val;
|
||||
|
||||
val = sd->ctrls[BRIGHTNESS].val;
|
||||
if (sd->sensor == SENSOR_OV767x) {
|
||||
if (val < 0)
|
||||
val = 0x80 - val;
|
||||
|
@ -1055,27 +877,18 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
||||
val = sd->ctrls[CONTRAST].val;
|
||||
if (sd->sensor == SENSOR_OV767x)
|
||||
sccb_reg_write(gspca_dev, 0x56, val); /* contras */
|
||||
else
|
||||
sccb_reg_write(gspca_dev, 0x9c, val);
|
||||
}
|
||||
|
||||
static void setgain(struct gspca_dev *gspca_dev)
|
||||
static void setgain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
||||
if (sd->ctrls[AGC].val)
|
||||
return;
|
||||
|
||||
val = sd->ctrls[GAIN].val;
|
||||
switch (val & 0x30) {
|
||||
case 0x00:
|
||||
val &= 0x0f;
|
||||
|
@ -1097,15 +910,15 @@ static void setgain(struct gspca_dev *gspca_dev)
|
|||
sccb_reg_write(gspca_dev, 0x00, val);
|
||||
}
|
||||
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static s32 getgain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
return sccb_reg_read(gspca_dev, 0x00);
|
||||
}
|
||||
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
||||
if (sd->ctrls[AEC].val)
|
||||
return;
|
||||
|
||||
val = sd->ctrls[EXPOSURE].val;
|
||||
if (sd->sensor == SENSOR_OV767x) {
|
||||
|
||||
/* set only aec[9:2] */
|
||||
|
@ -1123,11 +936,23 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setagc(struct gspca_dev *gspca_dev)
|
||||
static s32 getexposure(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->ctrls[AGC].val) {
|
||||
if (sd->sensor == SENSOR_OV767x) {
|
||||
/* get only aec[9:2] */
|
||||
return sccb_reg_read(gspca_dev, 0x10); /* aech */
|
||||
} else {
|
||||
u8 hi = sccb_reg_read(gspca_dev, 0x08);
|
||||
u8 lo = sccb_reg_read(gspca_dev, 0x10);
|
||||
return (hi << 8 | lo) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void setagc(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
if (val) {
|
||||
sccb_reg_write(gspca_dev, 0x13,
|
||||
sccb_reg_read(gspca_dev, 0x13) | 0x04);
|
||||
sccb_reg_write(gspca_dev, 0x64,
|
||||
|
@ -1137,16 +962,14 @@ static void setagc(struct gspca_dev *gspca_dev)
|
|||
sccb_reg_read(gspca_dev, 0x13) & ~0x04);
|
||||
sccb_reg_write(gspca_dev, 0x64,
|
||||
sccb_reg_read(gspca_dev, 0x64) & ~0x03);
|
||||
|
||||
setgain(gspca_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void setawb(struct gspca_dev *gspca_dev)
|
||||
static void setawb(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->ctrls[AWB].val) {
|
||||
if (val) {
|
||||
sccb_reg_write(gspca_dev, 0x13,
|
||||
sccb_reg_read(gspca_dev, 0x13) | 0x02);
|
||||
if (sd->sensor == SENSOR_OV772x)
|
||||
|
@ -1161,7 +984,7 @@ static void setawb(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setaec(struct gspca_dev *gspca_dev)
|
||||
static void setaec(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 data;
|
||||
|
@ -1169,31 +992,25 @@ static void setaec(struct gspca_dev *gspca_dev)
|
|||
data = sd->sensor == SENSOR_OV767x ?
|
||||
0x05 : /* agc + aec */
|
||||
0x01; /* agc */
|
||||
if (sd->ctrls[AEC].val)
|
||||
switch (val) {
|
||||
case V4L2_EXPOSURE_AUTO:
|
||||
sccb_reg_write(gspca_dev, 0x13,
|
||||
sccb_reg_read(gspca_dev, 0x13) | data);
|
||||
else {
|
||||
break;
|
||||
case V4L2_EXPOSURE_MANUAL:
|
||||
sccb_reg_write(gspca_dev, 0x13,
|
||||
sccb_reg_read(gspca_dev, 0x13) & ~data);
|
||||
if (sd->sensor == SENSOR_OV767x)
|
||||
sd->ctrls[EXPOSURE].val =
|
||||
sccb_reg_read(gspca_dev, 10); /* aech */
|
||||
else
|
||||
setexposure(gspca_dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void setsharpness(struct gspca_dev *gspca_dev)
|
||||
static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
||||
val = sd->ctrls[SHARPNESS].val;
|
||||
sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */
|
||||
sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */
|
||||
}
|
||||
|
||||
static void sethvflip(struct gspca_dev *gspca_dev)
|
||||
static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
@ -1201,28 +1018,27 @@ static void sethvflip(struct gspca_dev *gspca_dev)
|
|||
if (sd->sensor == SENSOR_OV767x) {
|
||||
val = sccb_reg_read(gspca_dev, 0x1e); /* mvfp */
|
||||
val &= ~0x30;
|
||||
if (sd->ctrls[HFLIP].val)
|
||||
if (hflip)
|
||||
val |= 0x20;
|
||||
if (sd->ctrls[VFLIP].val)
|
||||
if (vflip)
|
||||
val |= 0x10;
|
||||
sccb_reg_write(gspca_dev, 0x1e, val);
|
||||
} else {
|
||||
val = sccb_reg_read(gspca_dev, 0x0c);
|
||||
val &= ~0xc0;
|
||||
if (sd->ctrls[HFLIP].val == 0)
|
||||
if (hflip == 0)
|
||||
val |= 0x40;
|
||||
if (sd->ctrls[VFLIP].val == 0)
|
||||
if (vflip == 0)
|
||||
val |= 0x80;
|
||||
sccb_reg_write(gspca_dev, 0x0c, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev)
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
||||
val = sd->ctrls[LIGHTFREQ].val ? 0x9e : 0x00;
|
||||
val = val ? 0x9e : 0x00;
|
||||
if (sd->sensor == SENSOR_OV767x) {
|
||||
sccb_reg_write(gspca_dev, 0x2a, 0x00);
|
||||
if (val)
|
||||
|
@ -1241,8 +1057,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
|
||||
cam = &gspca_dev->cam;
|
||||
|
||||
cam->ctrls = sd->ctrls;
|
||||
|
||||
cam->cam_mode = ov772x_mode;
|
||||
cam->nmodes = ARRAY_SIZE(ov772x_mode);
|
||||
|
||||
|
@ -1251,6 +1065,195 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ov534_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
|
||||
struct gspca_dev *gspca_dev = &sd->gspca_dev;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
gspca_dev->usb_err = 0;
|
||||
if (ctrl->val && sd->gain && gspca_dev->streaming)
|
||||
sd->gain->val = getgain(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
gspca_dev->usb_err = 0;
|
||||
if (ctrl->val == V4L2_EXPOSURE_AUTO && sd->exposure &&
|
||||
gspca_dev->streaming)
|
||||
sd->exposure->val = getexposure(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ov534_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
|
||||
struct gspca_dev *gspca_dev = &sd->gspca_dev;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_HUE:
|
||||
sethue(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setsaturation(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
/* case V4L2_CID_GAIN: */
|
||||
setagc(gspca_dev, ctrl->val);
|
||||
if (!gspca_dev->usb_err && !ctrl->val && sd->gain)
|
||||
setgain(gspca_dev, sd->gain->val);
|
||||
break;
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
setawb(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
/* case V4L2_CID_EXPOSURE: */
|
||||
setaec(gspca_dev, ctrl->val);
|
||||
if (!gspca_dev->usb_err && ctrl->val == V4L2_EXPOSURE_MANUAL &&
|
||||
sd->exposure)
|
||||
setexposure(gspca_dev, sd->exposure->val);
|
||||
break;
|
||||
case V4L2_CID_SHARPNESS:
|
||||
setsharpness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
|
||||
break;
|
||||
case V4L2_CID_VFLIP:
|
||||
sethvflip(gspca_dev, sd->hflip->val, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
setlightfreq(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops ov534_ctrl_ops = {
|
||||
.g_volatile_ctrl = ov534_g_volatile_ctrl,
|
||||
.s_ctrl = ov534_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler;
|
||||
/* parameters with different values between the supported sensors */
|
||||
int saturation_min;
|
||||
int saturation_max;
|
||||
int saturation_def;
|
||||
int brightness_min;
|
||||
int brightness_max;
|
||||
int brightness_def;
|
||||
int contrast_max;
|
||||
int contrast_def;
|
||||
int exposure_min;
|
||||
int exposure_max;
|
||||
int exposure_def;
|
||||
int hflip_def;
|
||||
|
||||
if (sd->sensor == SENSOR_OV767x) {
|
||||
saturation_min = 0,
|
||||
saturation_max = 6,
|
||||
saturation_def = 3,
|
||||
brightness_min = -127;
|
||||
brightness_max = 127;
|
||||
brightness_def = 0;
|
||||
contrast_max = 0x80;
|
||||
contrast_def = 0x40;
|
||||
exposure_min = 0x08;
|
||||
exposure_max = 0x60;
|
||||
exposure_def = 0x13;
|
||||
hflip_def = 1;
|
||||
} else {
|
||||
saturation_min = 0,
|
||||
saturation_max = 255,
|
||||
saturation_def = 64,
|
||||
brightness_min = 0;
|
||||
brightness_max = 255;
|
||||
brightness_def = 0;
|
||||
contrast_max = 255;
|
||||
contrast_def = 32;
|
||||
exposure_min = 0;
|
||||
exposure_max = 255;
|
||||
exposure_def = 120;
|
||||
hflip_def = 0;
|
||||
}
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
|
||||
v4l2_ctrl_handler_init(hdl, 13);
|
||||
|
||||
if (sd->sensor == SENSOR_OV772x)
|
||||
sd->hue = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_HUE, -90, 90, 1, 0);
|
||||
|
||||
sd->saturation = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_SATURATION, saturation_min, saturation_max, 1,
|
||||
saturation_def);
|
||||
sd->brightness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, brightness_min, brightness_max, 1,
|
||||
brightness_def);
|
||||
sd->contrast = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, contrast_max, 1, contrast_def);
|
||||
|
||||
if (sd->sensor == SENSOR_OV772x) {
|
||||
sd->autogain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
sd->gain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 63, 1, 20);
|
||||
}
|
||||
|
||||
sd->autoexposure = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE_AUTO,
|
||||
V4L2_EXPOSURE_MANUAL, 0,
|
||||
V4L2_EXPOSURE_AUTO);
|
||||
sd->exposure = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, exposure_min, exposure_max, 1,
|
||||
exposure_def);
|
||||
|
||||
sd->autowhitebalance = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
|
||||
|
||||
if (sd->sensor == SENSOR_OV772x)
|
||||
sd->sharpness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_SHARPNESS, 0, 63, 1, 0);
|
||||
|
||||
sd->hflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, hflip_def);
|
||||
sd->vflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_50HZ, 0,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
|
||||
if (sd->sensor == SENSOR_OV772x)
|
||||
v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
|
||||
|
||||
v4l2_ctrl_auto_cluster(2, &sd->autoexposure, V4L2_EXPOSURE_MANUAL,
|
||||
true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this function is called at probe and resume time */
|
||||
static int sd_init(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
|
@ -1286,24 +1289,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
|
||||
if ((sensor_id & 0xfff0) == 0x7670) {
|
||||
sd->sensor = SENSOR_OV767x;
|
||||
gspca_dev->ctrl_dis = (1 << HUE) |
|
||||
(1 << GAIN) |
|
||||
(1 << AGC) |
|
||||
(1 << SHARPNESS); /* auto */
|
||||
sd->ctrls[SATURATION].min = 0,
|
||||
sd->ctrls[SATURATION].max = 6,
|
||||
sd->ctrls[SATURATION].def = 3,
|
||||
sd->ctrls[BRIGHTNESS].min = -127;
|
||||
sd->ctrls[BRIGHTNESS].max = 127;
|
||||
sd->ctrls[BRIGHTNESS].def = 0;
|
||||
sd->ctrls[CONTRAST].max = 0x80;
|
||||
sd->ctrls[CONTRAST].def = 0x40;
|
||||
sd->ctrls[EXPOSURE].min = 0x08;
|
||||
sd->ctrls[EXPOSURE].max = 0x60;
|
||||
sd->ctrls[EXPOSURE].def = 0x13;
|
||||
sd->ctrls[SHARPNESS].max = 9;
|
||||
sd->ctrls[SHARPNESS].def = 4;
|
||||
sd->ctrls[HFLIP].def = 1;
|
||||
gspca_dev->cam.cam_mode = ov767x_mode;
|
||||
gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode);
|
||||
} else {
|
||||
|
@ -1366,22 +1351,23 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
|
||||
set_frame_rate(gspca_dev);
|
||||
|
||||
if (!(gspca_dev->ctrl_dis & (1 << HUE)))
|
||||
sethue(gspca_dev);
|
||||
setsaturation(gspca_dev);
|
||||
if (!(gspca_dev->ctrl_dis & (1 << AGC)))
|
||||
setagc(gspca_dev);
|
||||
setawb(gspca_dev);
|
||||
setaec(gspca_dev);
|
||||
if (!(gspca_dev->ctrl_dis & (1 << GAIN)))
|
||||
setgain(gspca_dev);
|
||||
setexposure(gspca_dev);
|
||||
setbrightness(gspca_dev);
|
||||
setcontrast(gspca_dev);
|
||||
if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS)))
|
||||
setsharpness(gspca_dev);
|
||||
sethvflip(gspca_dev);
|
||||
setlightfreq(gspca_dev);
|
||||
if (sd->hue)
|
||||
sethue(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue));
|
||||
setsaturation(gspca_dev, v4l2_ctrl_g_ctrl(sd->saturation));
|
||||
if (sd->autogain)
|
||||
setagc(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
|
||||
setawb(gspca_dev, v4l2_ctrl_g_ctrl(sd->autowhitebalance));
|
||||
setaec(gspca_dev, v4l2_ctrl_g_ctrl(sd->autoexposure));
|
||||
if (sd->gain)
|
||||
setgain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
|
||||
setbrightness(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness));
|
||||
setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
|
||||
if (sd->sharpness)
|
||||
setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
|
||||
sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
|
||||
v4l2_ctrl_g_ctrl(sd->vflip));
|
||||
setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
|
||||
|
||||
ov534_set_led(gspca_dev, 1);
|
||||
ov534_reg_write(gspca_dev, 0xe0, 0x00);
|
||||
|
@ -1483,25 +1469,6 @@ scan_next:
|
|||
} while (remaining_len > 0);
|
||||
}
|
||||
|
||||
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_querymenu *menu)
|
||||
{
|
||||
switch (menu->id) {
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
switch (menu->index) {
|
||||
case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
|
||||
strcpy((char *) menu->name, "Disabled");
|
||||
return 0;
|
||||
case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
|
||||
strcpy((char *) menu->name, "50 Hz");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* get stream parameters (framerate) */
|
||||
static void sd_get_streamparm(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_streamparm *parm)
|
||||
|
@ -1536,14 +1503,12 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
|
|||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.querymenu = sd_querymenu,
|
||||
.get_streamparm = sd_get_streamparm,
|
||||
.set_streamparm = sd_set_streamparm,
|
||||
};
|
||||
|
@ -1572,6 +1537,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -47,22 +47,9 @@ MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
|
|||
MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* controls */
|
||||
enum e_ctrl {
|
||||
BRIGHTNESS,
|
||||
CONTRAST,
|
||||
AUTOGAIN,
|
||||
EXPOSURE,
|
||||
SHARPNESS,
|
||||
SATUR,
|
||||
LIGHTFREQ,
|
||||
NCTRLS /* number of controls */
|
||||
};
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
struct gspca_ctrl ctrls[NCTRLS];
|
||||
__u32 last_pts;
|
||||
u8 last_fid;
|
||||
|
||||
|
@ -75,103 +62,6 @@ enum sensors {
|
|||
NSENSORS
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static void setbrightness(struct gspca_dev *gspca_dev);
|
||||
static void setcontrast(struct gspca_dev *gspca_dev);
|
||||
static void setautogain(struct gspca_dev *gspca_dev);
|
||||
static void setexposure(struct gspca_dev *gspca_dev);
|
||||
static void setsharpness(struct gspca_dev *gspca_dev);
|
||||
static void setsatur(struct gspca_dev *gspca_dev);
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev);
|
||||
|
||||
static const struct ctrl sd_ctrls[NCTRLS] = {
|
||||
[BRIGHTNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 15,
|
||||
.step = 1,
|
||||
.default_value = 7
|
||||
},
|
||||
.set_control = setbrightness
|
||||
},
|
||||
[CONTRAST] = {
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 15,
|
||||
.step = 1,
|
||||
.default_value = 3
|
||||
},
|
||||
.set_control = setcontrast
|
||||
},
|
||||
[AUTOGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Autogain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
#define AUTOGAIN_DEF 1
|
||||
.default_value = AUTOGAIN_DEF,
|
||||
},
|
||||
.set_control = setautogain
|
||||
},
|
||||
[EXPOSURE] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
.default_value = 0
|
||||
},
|
||||
.set_control = setexposure
|
||||
},
|
||||
[SHARPNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_SHARPNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Sharpness",
|
||||
.minimum = -1, /* -1 = auto */
|
||||
.maximum = 4,
|
||||
.step = 1,
|
||||
.default_value = -1
|
||||
},
|
||||
.set_control = setsharpness
|
||||
},
|
||||
[SATUR] = {
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
.maximum = 4,
|
||||
.step = 1,
|
||||
.default_value = 2
|
||||
},
|
||||
.set_control = setsatur
|
||||
},
|
||||
[LIGHTFREQ] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Light frequency filter",
|
||||
.minimum = 0,
|
||||
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
|
||||
.step = 1,
|
||||
.default_value = 0
|
||||
},
|
||||
.set_control = setlightfreq
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format ov965x_mode[] = {
|
||||
#define QVGA_MODE 0
|
||||
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
|
||||
|
@ -1104,16 +994,14 @@ static void set_led(struct gspca_dev *gspca_dev, int status)
|
|||
}
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
s8 sval;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
|
||||
return;
|
||||
if (sd->sensor == SENSOR_OV562x) {
|
||||
sval = sd->ctrls[BRIGHTNESS].val;
|
||||
sval = brightness;
|
||||
val = 0x76;
|
||||
val += sval;
|
||||
sccb_write(gspca_dev, 0x24, val);
|
||||
|
@ -1128,7 +1016,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
val = 0xe6;
|
||||
sccb_write(gspca_dev, 0x26, val);
|
||||
} else {
|
||||
val = sd->ctrls[BRIGHTNESS].val;
|
||||
val = brightness;
|
||||
if (val < 8)
|
||||
val = 15 - val; /* f .. 8 */
|
||||
else
|
||||
|
@ -1138,43 +1026,32 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << CONTRAST))
|
||||
return;
|
||||
sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */
|
||||
sd->ctrls[CONTRAST].val << 4);
|
||||
val << 4);
|
||||
}
|
||||
|
||||
static void setautogain(struct gspca_dev *gspca_dev)
|
||||
static void setautogain(struct gspca_dev *gspca_dev, s32 autogain)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
|
||||
return;
|
||||
/*fixme: should adjust agc/awb/aec by different controls */
|
||||
val = sccb_read(gspca_dev, 0x13); /* com8 */
|
||||
sccb_write(gspca_dev, 0xff, 0x00);
|
||||
if (sd->ctrls[AUTOGAIN].val)
|
||||
if (autogain)
|
||||
val |= 0x05; /* agc & aec */
|
||||
else
|
||||
val &= 0xfa;
|
||||
sccb_write(gspca_dev, 0x13, val);
|
||||
}
|
||||
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 exposure)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
|
||||
u8 val;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << EXPOSURE))
|
||||
return;
|
||||
sccb_write(gspca_dev, 0x10, /* aec[9:2] */
|
||||
expo[sd->ctrls[EXPOSURE].val]);
|
||||
sccb_write(gspca_dev, 0x10, expo[exposure]); /* aec[9:2] */
|
||||
|
||||
val = sccb_read(gspca_dev, 0x13); /* com8 */
|
||||
sccb_write(gspca_dev, 0xff, 0x00);
|
||||
|
@ -1185,14 +1062,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */
|
||||
}
|
||||
|
||||
static void setsharpness(struct gspca_dev *gspca_dev)
|
||||
static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
s8 val;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << SHARPNESS))
|
||||
return;
|
||||
val = sd->ctrls[SHARPNESS].val;
|
||||
if (val < 0) { /* auto */
|
||||
val = sccb_read(gspca_dev, 0x42); /* com17 */
|
||||
sccb_write(gspca_dev, 0xff, 0x00);
|
||||
|
@ -1209,9 +1080,8 @@ static void setsharpness(struct gspca_dev *gspca_dev)
|
|||
sccb_write(gspca_dev, 0x42, val & 0xbf);
|
||||
}
|
||||
|
||||
static void setsatur(struct gspca_dev *gspca_dev)
|
||||
static void setsatur(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val1, val2, val3;
|
||||
static const u8 matrix[5][2] = {
|
||||
{0x14, 0x38},
|
||||
|
@ -1221,10 +1091,8 @@ static void setsatur(struct gspca_dev *gspca_dev)
|
|||
{0x48, 0x90}
|
||||
};
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << SATUR))
|
||||
return;
|
||||
val1 = matrix[sd->ctrls[SATUR].val][0];
|
||||
val2 = matrix[sd->ctrls[SATUR].val][1];
|
||||
val1 = matrix[val][0];
|
||||
val2 = matrix[val][1];
|
||||
val3 = val1 + val2;
|
||||
sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */
|
||||
sccb_write(gspca_dev, 0x50, val3);
|
||||
|
@ -1239,16 +1107,13 @@ static void setsatur(struct gspca_dev *gspca_dev)
|
|||
sccb_write(gspca_dev, 0x41, val1);
|
||||
}
|
||||
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev)
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << LIGHTFREQ))
|
||||
return;
|
||||
val = sccb_read(gspca_dev, 0x13); /* com8 */
|
||||
sccb_write(gspca_dev, 0xff, 0x00);
|
||||
if (sd->ctrls[LIGHTFREQ].val == 0) {
|
||||
if (freq == 0) {
|
||||
sccb_write(gspca_dev, 0x13, val & 0xdf);
|
||||
return;
|
||||
}
|
||||
|
@ -1256,7 +1121,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
|
|||
|
||||
val = sccb_read(gspca_dev, 0x42); /* com17 */
|
||||
sccb_write(gspca_dev, 0xff, 0x00);
|
||||
if (sd->ctrls[LIGHTFREQ].val == 1)
|
||||
if (freq == 1)
|
||||
val |= 0x01;
|
||||
else
|
||||
val &= 0xfe;
|
||||
|
@ -1267,13 +1132,6 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
|
|||
static int sd_config(struct gspca_dev *gspca_dev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
gspca_dev->cam.ctrls = sd->ctrls;
|
||||
|
||||
#if AUTOGAIN_DEF != 0
|
||||
gspca_dev->ctrl_inac |= (1 << EXPOSURE);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1330,9 +1188,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
gspca_dev->cam.cam_mode = ov971x_mode;
|
||||
gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode);
|
||||
|
||||
/* no control yet */
|
||||
gspca_dev->ctrl_dis = (1 << NCTRLS) - 1;
|
||||
|
||||
gspca_dev->cam.bulk = 1;
|
||||
gspca_dev->cam.bulk_size = 16384;
|
||||
gspca_dev->cam.bulk_nurbs = 2;
|
||||
|
@ -1358,16 +1213,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x56, 0x17);
|
||||
} else if ((sensor_id & 0xfff0) == 0x5620) {
|
||||
sd->sensor = SENSOR_OV562x;
|
||||
gspca_dev->ctrl_dis = (1 << CONTRAST) |
|
||||
(1 << AUTOGAIN) |
|
||||
(1 << EXPOSURE) |
|
||||
(1 << SHARPNESS) |
|
||||
(1 << SATUR) |
|
||||
(1 << LIGHTFREQ);
|
||||
|
||||
sd->ctrls[BRIGHTNESS].min = -90;
|
||||
sd->ctrls[BRIGHTNESS].max = 90;
|
||||
sd->ctrls[BRIGHTNESS].def = 0;
|
||||
gspca_dev->cam.cam_mode = ov562x_mode;
|
||||
gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode);
|
||||
|
||||
|
@ -1390,10 +1235,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
|
||||
if (sd->sensor == SENSOR_OV971x)
|
||||
return gspca_dev->usb_err;
|
||||
else if (sd->sensor == SENSOR_OV562x) {
|
||||
setbrightness(gspca_dev);
|
||||
if (sd->sensor == SENSOR_OV562x)
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
switch (gspca_dev->curr_mode) {
|
||||
case QVGA_MODE: /* 320x240 */
|
||||
sccb_w_array(gspca_dev, ov965x_start_1_vga,
|
||||
|
@ -1437,13 +1281,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
ARRAY_SIZE(ov965x_start_2_sxga));
|
||||
break;
|
||||
}
|
||||
setlightfreq(gspca_dev);
|
||||
setautogain(gspca_dev);
|
||||
setbrightness(gspca_dev);
|
||||
setcontrast(gspca_dev);
|
||||
setexposure(gspca_dev);
|
||||
setsharpness(gspca_dev);
|
||||
setsatur(gspca_dev);
|
||||
|
||||
reg_w(gspca_dev, 0xe0, 0x00);
|
||||
reg_w(gspca_dev, 0xe0, 0x00);
|
||||
|
@ -1541,38 +1378,94 @@ scan_next:
|
|||
} while (remaining_len > 0);
|
||||
}
|
||||
|
||||
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_querymenu *menu)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
switch (menu->id) {
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setsatur(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
switch (menu->index) {
|
||||
case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
|
||||
strcpy((char *) menu->name, "NoFliker");
|
||||
return 0;
|
||||
case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
|
||||
strcpy((char *) menu->name, "50 Hz");
|
||||
return 0;
|
||||
case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
|
||||
strcpy((char *) menu->name, "60 Hz");
|
||||
return 0;
|
||||
}
|
||||
setlightfreq(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SHARPNESS:
|
||||
setsharpness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
if (ctrl->is_new)
|
||||
setautogain(gspca_dev, ctrl->val);
|
||||
if (!ctrl->val && gspca_dev->exposure->is_new)
|
||||
setexposure(gspca_dev, gspca_dev->exposure->val);
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
if (sd->sensor == SENSOR_OV971x)
|
||||
return 0;
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 7);
|
||||
if (sd->sensor == SENSOR_OV562x) {
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, -90, 90, 1, 0);
|
||||
} else {
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 15, 1, 7);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 15, 1, 3);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 4, 1, 2);
|
||||
/* -1 = auto */
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SHARPNESS, -1, 4, 1, -1);
|
||||
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 3, 1, 0);
|
||||
v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
|
||||
v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
|
||||
}
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = NCTRLS,
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.querymenu = sd_querymenu,
|
||||
};
|
||||
|
||||
/* -- module initialisation -- */
|
||||
|
@ -1600,6 +1493,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -462,6 +462,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -84,31 +84,31 @@
|
|||
/* Include pac common sof detection functions */
|
||||
#include "pac_common.h"
|
||||
|
||||
#define PAC7302_GAIN_DEFAULT 15
|
||||
#define PAC7302_GAIN_KNEE 42
|
||||
#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */
|
||||
#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
|
||||
|
||||
MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
|
||||
"Thomas Kaiser thomas@kaiser-linux.li");
|
||||
MODULE_DESCRIPTION("Pixart PAC7302");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
enum e_ctrl {
|
||||
BRIGHTNESS,
|
||||
CONTRAST,
|
||||
COLORS,
|
||||
WHITE_BALANCE,
|
||||
RED_BALANCE,
|
||||
BLUE_BALANCE,
|
||||
GAIN,
|
||||
AUTOGAIN,
|
||||
EXPOSURE,
|
||||
VFLIP,
|
||||
HFLIP,
|
||||
NCTRLS /* number of controls */
|
||||
};
|
||||
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
struct gspca_ctrl ctrls[NCTRLS];
|
||||
|
||||
struct { /* brightness / contrast cluster */
|
||||
struct v4l2_ctrl *brightness;
|
||||
struct v4l2_ctrl *contrast;
|
||||
};
|
||||
struct v4l2_ctrl *saturation;
|
||||
struct v4l2_ctrl *white_balance;
|
||||
struct v4l2_ctrl *red_balance;
|
||||
struct v4l2_ctrl *blue_balance;
|
||||
struct { /* flip cluster */
|
||||
struct v4l2_ctrl *hflip;
|
||||
struct v4l2_ctrl *vflip;
|
||||
};
|
||||
u8 flags;
|
||||
#define FL_HFLIP 0x01 /* mirrored by default */
|
||||
#define FL_VFLIP 0x02 /* vertical flipped by default */
|
||||
|
@ -119,160 +119,6 @@ struct sd {
|
|||
atomic_t avg_lum;
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static void setbrightcont(struct gspca_dev *gspca_dev);
|
||||
static void setcolors(struct gspca_dev *gspca_dev);
|
||||
static void setwhitebalance(struct gspca_dev *gspca_dev);
|
||||
static void setredbalance(struct gspca_dev *gspca_dev);
|
||||
static void setbluebalance(struct gspca_dev *gspca_dev);
|
||||
static void setgain(struct gspca_dev *gspca_dev);
|
||||
static void setexposure(struct gspca_dev *gspca_dev);
|
||||
static void setautogain(struct gspca_dev *gspca_dev);
|
||||
static void sethvflip(struct gspca_dev *gspca_dev);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
[BRIGHTNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
#define BRIGHTNESS_MAX 0x20
|
||||
.maximum = BRIGHTNESS_MAX,
|
||||
.step = 1,
|
||||
.default_value = 0x10,
|
||||
},
|
||||
.set_control = setbrightcont
|
||||
},
|
||||
[CONTRAST] = {
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
#define CONTRAST_MAX 255
|
||||
.maximum = CONTRAST_MAX,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setbrightcont
|
||||
},
|
||||
[COLORS] = {
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
#define COLOR_MAX 255
|
||||
.maximum = COLOR_MAX,
|
||||
.step = 1,
|
||||
.default_value = 127
|
||||
},
|
||||
.set_control = setcolors
|
||||
},
|
||||
[WHITE_BALANCE] = {
|
||||
{
|
||||
.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "White Balance",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 4,
|
||||
},
|
||||
.set_control = setwhitebalance
|
||||
},
|
||||
[RED_BALANCE] = {
|
||||
{
|
||||
.id = V4L2_CID_RED_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Red",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
.set_control = setredbalance
|
||||
},
|
||||
[BLUE_BALANCE] = {
|
||||
{
|
||||
.id = V4L2_CID_BLUE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Blue",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
.set_control = setbluebalance
|
||||
},
|
||||
[GAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 62,
|
||||
.step = 1,
|
||||
#define GAIN_DEF 15
|
||||
#define GAIN_KNEE 46
|
||||
.default_value = GAIN_DEF,
|
||||
},
|
||||
.set_control = setgain
|
||||
},
|
||||
[EXPOSURE] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 1023,
|
||||
.step = 1,
|
||||
#define EXPOSURE_DEF 66 /* 33 ms / 30 fps */
|
||||
#define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
|
||||
.default_value = EXPOSURE_DEF,
|
||||
},
|
||||
.set_control = setexposure
|
||||
},
|
||||
[AUTOGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
#define AUTOGAIN_DEF 1
|
||||
.default_value = AUTOGAIN_DEF,
|
||||
},
|
||||
.set_control = setautogain,
|
||||
},
|
||||
[HFLIP] = {
|
||||
{
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Mirror",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = sethvflip,
|
||||
},
|
||||
[VFLIP] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Vflip",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = sethvflip
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
|
||||
.bytesperline = 640,
|
||||
|
@ -516,8 +362,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->cam_mode = vga_mode; /* only 640x480 */
|
||||
cam->nmodes = ARRAY_SIZE(vga_mode);
|
||||
|
||||
gspca_dev->cam.ctrls = sd->ctrls;
|
||||
|
||||
sd->flags = id->driver_info;
|
||||
return 0;
|
||||
}
|
||||
|
@ -536,9 +380,9 @@ static void setbrightcont(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
||||
for (i = 0; i < 10; i++) {
|
||||
v = max[i];
|
||||
v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX)
|
||||
* 150 / BRIGHTNESS_MAX; /* 200 ? */
|
||||
v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX;
|
||||
v += (sd->brightness->val - sd->brightness->maximum)
|
||||
* 150 / sd->brightness->maximum; /* 200 ? */
|
||||
v -= delta[i] * sd->contrast->val / sd->contrast->maximum;
|
||||
if (v < 0)
|
||||
v = 0;
|
||||
else if (v > 0xff)
|
||||
|
@ -561,7 +405,8 @@ static void setcolors(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x11, 0x01);
|
||||
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
||||
for (i = 0; i < 9; i++) {
|
||||
v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i];
|
||||
v = a[i] * sd->saturation->val / sd->saturation->maximum;
|
||||
v += b[i];
|
||||
reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
|
||||
reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
|
||||
}
|
||||
|
@ -573,7 +418,7 @@ static void setwhitebalance(struct gspca_dev *gspca_dev)
|
|||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
||||
reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val);
|
||||
reg_w(gspca_dev, 0xc6, sd->white_balance->val);
|
||||
|
||||
reg_w(gspca_dev, 0xdc, 0x01);
|
||||
}
|
||||
|
@ -583,7 +428,7 @@ static void setredbalance(struct gspca_dev *gspca_dev)
|
|||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
||||
reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val);
|
||||
reg_w(gspca_dev, 0xc5, sd->red_balance->val);
|
||||
|
||||
reg_w(gspca_dev, 0xdc, 0x01);
|
||||
}
|
||||
|
@ -593,22 +438,21 @@ static void setbluebalance(struct gspca_dev *gspca_dev)
|
|||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
|
||||
reg_w(gspca_dev, 0xc7, sd->ctrls[BLUE_BALANCE].val);
|
||||
reg_w(gspca_dev, 0xc7, sd->blue_balance->val);
|
||||
|
||||
reg_w(gspca_dev, 0xdc, 0x01);
|
||||
}
|
||||
|
||||
static void setgain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 reg10, reg12;
|
||||
|
||||
if (sd->ctrls[GAIN].val < 32) {
|
||||
reg10 = sd->ctrls[GAIN].val;
|
||||
if (gspca_dev->gain->val < 32) {
|
||||
reg10 = gspca_dev->gain->val;
|
||||
reg12 = 0;
|
||||
} else {
|
||||
reg10 = 31;
|
||||
reg12 = sd->ctrls[GAIN].val - 31;
|
||||
reg12 = gspca_dev->gain->val - 31;
|
||||
}
|
||||
|
||||
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
|
||||
|
@ -621,7 +465,6 @@ static void setgain(struct gspca_dev *gspca_dev)
|
|||
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 clockdiv;
|
||||
u16 exposure;
|
||||
|
||||
|
@ -630,7 +473,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
* no fps according to the formula: 90 / reg. sd->exposure is the
|
||||
* desired exposure time in 0.5 ms.
|
||||
*/
|
||||
clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000;
|
||||
clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000;
|
||||
|
||||
/*
|
||||
* Note clockdiv = 3 also works, but when running at 30 fps, depending
|
||||
|
@ -655,7 +498,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
* frame exposure time in ms = 1000 * clockdiv / 90 ->
|
||||
* exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90)
|
||||
*/
|
||||
exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv);
|
||||
exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv);
|
||||
/* 0 = use full frametime, 448 = no exposure, reverse it */
|
||||
exposure = 448 - exposure;
|
||||
|
||||
|
@ -668,37 +511,15 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x11, 0x01);
|
||||
}
|
||||
|
||||
static void setautogain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
/*
|
||||
* When switching to autogain set defaults to make sure
|
||||
* we are on a valid point of the autogain gain /
|
||||
* exposure knee graph, and give this change time to
|
||||
* take effect before doing autogain.
|
||||
*/
|
||||
if (sd->ctrls[AUTOGAIN].val) {
|
||||
sd->ctrls[EXPOSURE].val = EXPOSURE_DEF;
|
||||
sd->ctrls[GAIN].val = GAIN_DEF;
|
||||
sd->autogain_ignore_frames =
|
||||
PAC_AUTOGAIN_IGNORE_FRAMES;
|
||||
} else {
|
||||
sd->autogain_ignore_frames = -1;
|
||||
}
|
||||
setexposure(gspca_dev);
|
||||
setgain(gspca_dev);
|
||||
}
|
||||
|
||||
static void sethvflip(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 data, hflip, vflip;
|
||||
|
||||
hflip = sd->ctrls[HFLIP].val;
|
||||
hflip = sd->hflip->val;
|
||||
if (sd->flags & FL_HFLIP)
|
||||
hflip = !hflip;
|
||||
vflip = sd->ctrls[VFLIP].val;
|
||||
vflip = sd->vflip->val;
|
||||
if (sd->flags & FL_VFLIP)
|
||||
vflip = !vflip;
|
||||
|
||||
|
@ -717,6 +538,112 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
|
||||
/* when switching to autogain set defaults to make sure
|
||||
we are on a valid point of the autogain gain /
|
||||
exposure knee graph, and give this change time to
|
||||
take effect before doing autogain. */
|
||||
gspca_dev->exposure->val = PAC7302_EXPOSURE_DEFAULT;
|
||||
gspca_dev->gain->val = PAC7302_GAIN_DEFAULT;
|
||||
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
|
||||
}
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightcont(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolors(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
|
||||
setwhitebalance(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
setredbalance(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
setbluebalance(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
|
||||
setexposure(gspca_dev);
|
||||
if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
|
||||
setgain(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
sethvflip(gspca_dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
/* this function is called at probe time */
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 11);
|
||||
|
||||
sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 32, 1, 16);
|
||||
sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 255, 1, 127);
|
||||
|
||||
sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 255, 1, 127);
|
||||
sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_WHITE_BALANCE_TEMPERATURE,
|
||||
0, 255, 1, 4);
|
||||
sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
|
||||
sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
|
||||
|
||||
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 1023, 1,
|
||||
PAC7302_EXPOSURE_DEFAULT);
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 62, 1,
|
||||
PAC7302_GAIN_DEFAULT);
|
||||
|
||||
sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
|
||||
v4l2_ctrl_cluster(2, &sd->brightness);
|
||||
v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
|
||||
v4l2_ctrl_cluster(2, &sd->hflip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -- start the camera -- */
|
||||
static int sd_start(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
@ -728,11 +655,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
setwhitebalance(gspca_dev);
|
||||
setredbalance(gspca_dev);
|
||||
setbluebalance(gspca_dev);
|
||||
setautogain(gspca_dev);
|
||||
setexposure(gspca_dev);
|
||||
setgain(gspca_dev);
|
||||
sethvflip(gspca_dev);
|
||||
|
||||
sd->sof_read = 0;
|
||||
atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val);
|
||||
sd->autogain_ignore_frames = 0;
|
||||
atomic_set(&sd->avg_lum, 270 + sd->brightness->val);
|
||||
|
||||
/* start stream */
|
||||
reg_w(gspca_dev, 0xff, 0x01);
|
||||
|
@ -758,9 +687,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x78, 0x40);
|
||||
}
|
||||
|
||||
#define WANT_REGULAR_AUTOGAIN
|
||||
#include "autogain_functions.h"
|
||||
|
||||
static void do_autogain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
@ -774,11 +700,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
|
|||
if (sd->autogain_ignore_frames > 0) {
|
||||
sd->autogain_ignore_frames--;
|
||||
} else {
|
||||
desired_lum = 270 + sd->ctrls[BRIGHTNESS].val;
|
||||
desired_lum = 270 + sd->brightness->val;
|
||||
|
||||
auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
|
||||
deadzone, GAIN_KNEE, EXPOSURE_KNEE);
|
||||
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
|
||||
if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum,
|
||||
deadzone, PAC7302_GAIN_KNEE,
|
||||
PAC7302_EXPOSURE_KNEE))
|
||||
sd->autogain_ignore_frames =
|
||||
PAC_AUTOGAIN_IGNORE_FRAMES;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -944,10 +872,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
/* sub-driver description for pac7302 */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.stop0 = sd_stop0,
|
||||
|
@ -998,6 +925,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -694,6 +694,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -45,15 +45,6 @@ MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
|||
MODULE_DESCRIPTION("Endpoints se401");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* controls */
|
||||
enum e_ctrl {
|
||||
BRIGHTNESS,
|
||||
GAIN,
|
||||
EXPOSURE,
|
||||
FREQ,
|
||||
NCTRL /* number of controls */
|
||||
};
|
||||
|
||||
/* exposure change state machine states */
|
||||
enum {
|
||||
EXPO_CHANGED,
|
||||
|
@ -64,7 +55,11 @@ enum {
|
|||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
struct gspca_ctrl ctrls[NCTRL];
|
||||
struct { /* exposure/freq control cluster */
|
||||
struct v4l2_ctrl *exposure;
|
||||
struct v4l2_ctrl *freq;
|
||||
};
|
||||
bool has_brightness;
|
||||
struct v4l2_pix_format fmts[MAX_MODES];
|
||||
int pixels_read;
|
||||
int packet_read;
|
||||
|
@ -77,60 +72,6 @@ struct sd {
|
|||
int expo_change_state;
|
||||
};
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev);
|
||||
static void setgain(struct gspca_dev *gspca_dev);
|
||||
static void setexposure(struct gspca_dev *gspca_dev);
|
||||
|
||||
static const struct ctrl sd_ctrls[NCTRL] = {
|
||||
[BRIGHTNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 15,
|
||||
},
|
||||
.set_control = setbrightness
|
||||
},
|
||||
[GAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 50, /* Really 63 but > 50 is not pretty */
|
||||
.step = 1,
|
||||
.default_value = 25,
|
||||
},
|
||||
.set_control = setgain
|
||||
},
|
||||
[EXPOSURE] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 32767,
|
||||
.step = 1,
|
||||
.default_value = 15000,
|
||||
},
|
||||
.set_control = setexposure
|
||||
},
|
||||
[FREQ] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Light frequency filter",
|
||||
.minimum = 0,
|
||||
.maximum = 2,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set_control = setexposure
|
||||
},
|
||||
};
|
||||
|
||||
static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value,
|
||||
int silent)
|
||||
|
@ -224,22 +165,15 @@ static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
|
|||
return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
|
||||
return;
|
||||
|
||||
/* HDG: this does not seem to do anything on my cam */
|
||||
se401_write_req(gspca_dev, SE401_REQ_SET_BRT,
|
||||
sd->ctrls[BRIGHTNESS].val, 0);
|
||||
se401_write_req(gspca_dev, SE401_REQ_SET_BRT, val, 0);
|
||||
}
|
||||
|
||||
static void setgain(struct gspca_dev *gspca_dev)
|
||||
static void setgain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u16 gain = 63 - sd->ctrls[GAIN].val;
|
||||
u16 gain = 63 - val;
|
||||
|
||||
/* red color gain */
|
||||
se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain);
|
||||
|
@ -249,10 +183,10 @@ static void setgain(struct gspca_dev *gspca_dev)
|
|||
se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain);
|
||||
}
|
||||
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int integration = sd->ctrls[EXPOSURE].val << 6;
|
||||
int integration = val << 6;
|
||||
u8 expose_h, expose_m, expose_l;
|
||||
|
||||
/* Do this before the set_feature calls, for proper timing wrt
|
||||
|
@ -262,9 +196,9 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
through so be it */
|
||||
sd->expo_change_state = EXPO_CHANGED;
|
||||
|
||||
if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
|
||||
if (freq == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
|
||||
integration = integration - integration % 106667;
|
||||
if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
|
||||
if (freq == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
|
||||
integration = integration - integration % 88889;
|
||||
|
||||
expose_h = (integration >> 16);
|
||||
|
@ -375,15 +309,12 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->bulk = 1;
|
||||
cam->bulk_size = BULK_SIZE;
|
||||
cam->bulk_nurbs = 4;
|
||||
cam->ctrls = sd->ctrls;
|
||||
sd->resetlevel = 0x2d; /* Set initial resetlevel */
|
||||
|
||||
/* See if the camera supports brightness */
|
||||
se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1);
|
||||
if (gspca_dev->usb_err) {
|
||||
gspca_dev->ctrl_dis = (1 << BRIGHTNESS);
|
||||
gspca_dev->usb_err = 0;
|
||||
}
|
||||
sd->has_brightness = !!gspca_dev->usb_err;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -442,9 +373,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode);
|
||||
|
||||
setbrightness(gspca_dev);
|
||||
setgain(gspca_dev);
|
||||
setexposure(gspca_dev);
|
||||
se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
|
||||
|
||||
sd->packet_read = 0;
|
||||
|
@ -666,27 +594,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
|
|||
sd_pkt_scan_janggu(gspca_dev, data, len);
|
||||
}
|
||||
|
||||
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_querymenu *menu)
|
||||
{
|
||||
switch (menu->id) {
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
switch (menu->index) {
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
|
||||
strcpy((char *) menu->name, "NoFliker");
|
||||
return 0;
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
|
||||
strcpy((char *) menu->name, "50 Hz");
|
||||
return 0;
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
|
||||
strcpy((char *) menu->name, "60 Hz");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
||||
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
|
||||
{
|
||||
|
@ -714,19 +621,73 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
setgain(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
setexposure(gspca_dev, ctrl->val, sd->freq->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 4);
|
||||
if (sd->has_brightness)
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 15);
|
||||
/* max is really 63 but > 50 is not pretty */
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 50, 1, 25);
|
||||
sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 32767, 1, 15000);
|
||||
sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
v4l2_ctrl_cluster(2, &sd->exposure);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.isoc_init = sd_isoc_init,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.dq_callback = sd_dq_callback,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.querymenu = sd_querymenu,
|
||||
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
||||
.int_pkt_scan = sd_int_pkt_scan,
|
||||
#endif
|
||||
|
@ -769,6 +730,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
.pre_reset = sd_pre_reset,
|
||||
.post_reset = sd_post_reset,
|
||||
|
|
|
@ -40,10 +40,6 @@ struct init_command {
|
|||
unsigned char to_read; /* length to read. 0 means no reply requested */
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
};
|
||||
|
||||
/* How to change the resolution of any of the VGA cams is unknown */
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
|
||||
|
@ -695,8 +691,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.start = sd_start,
|
||||
|
@ -734,6 +728,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -56,26 +56,16 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
|
|||
MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* controls */
|
||||
enum e_ctrl {
|
||||
BRIGHTNESS,
|
||||
GAIN,
|
||||
EXPOSURE,
|
||||
AUTOGAIN,
|
||||
FREQ,
|
||||
NCTRLS /* number of controls */
|
||||
};
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
struct gspca_ctrl ctrls[NCTRLS];
|
||||
struct v4l2_ctrl *brightness;
|
||||
struct v4l2_ctrl *plfreq;
|
||||
|
||||
atomic_t avg_lum;
|
||||
int prev_avg_lum;
|
||||
int exp_too_low_cnt;
|
||||
int exp_too_high_cnt;
|
||||
int exposure_knee;
|
||||
int header_read;
|
||||
u8 header[12]; /* Header without sof marker */
|
||||
|
||||
|
@ -107,24 +97,16 @@ struct sensor_data {
|
|||
sensor_init_t *sensor_init;
|
||||
int sensor_init_size;
|
||||
int flags;
|
||||
unsigned ctrl_dis;
|
||||
__u8 sensor_addr;
|
||||
};
|
||||
|
||||
/* sensor_data flags */
|
||||
#define F_GAIN 0x01 /* has gain */
|
||||
#define F_SIF 0x02 /* sif or vga */
|
||||
#define F_COARSE_EXPO 0x04 /* exposure control is coarse */
|
||||
#define F_SIF 0x01 /* sif or vga */
|
||||
|
||||
/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */
|
||||
#define MODE_RAW 0x10 /* raw bayer mode */
|
||||
#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */
|
||||
|
||||
/* ctrl_dis helper macros */
|
||||
#define NO_EXPO ((1 << EXPOSURE) | (1 << AUTOGAIN))
|
||||
#define NO_FREQ (1 << FREQ)
|
||||
#define NO_BRIGHTNESS (1 << BRIGHTNESS)
|
||||
|
||||
#define COMP 0xc7 /* 0x87 //0x07 */
|
||||
#define COMP1 0xc9 /* 0x89 //0x09 */
|
||||
|
||||
|
@ -133,12 +115,12 @@ struct sensor_data {
|
|||
|
||||
#define SYS_CLK 0x04
|
||||
|
||||
#define SENS(bridge, sensor, _flags, _ctrl_dis, _sensor_addr) \
|
||||
#define SENS(bridge, sensor, _flags, _sensor_addr) \
|
||||
{ \
|
||||
.bridge_init = bridge, \
|
||||
.sensor_init = sensor, \
|
||||
.sensor_init_size = sizeof(sensor), \
|
||||
.flags = _flags, .ctrl_dis = _ctrl_dis, .sensor_addr = _sensor_addr \
|
||||
.flags = _flags, .sensor_addr = _sensor_addr \
|
||||
}
|
||||
|
||||
/* We calculate the autogain at the end of the transfer of a frame, at this
|
||||
|
@ -147,87 +129,6 @@ struct sensor_data {
|
|||
the new settings to come into effect before doing any other adjustments. */
|
||||
#define AUTOGAIN_IGNORE_FRAMES 1
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static void setbrightness(struct gspca_dev *gspca_dev);
|
||||
static void setgain(struct gspca_dev *gspca_dev);
|
||||
static void setexposure(struct gspca_dev *gspca_dev);
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static void setfreq(struct gspca_dev *gspca_dev);
|
||||
|
||||
static const struct ctrl sd_ctrls[NCTRLS] = {
|
||||
[BRIGHTNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setbrightness
|
||||
},
|
||||
[GAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define GAIN_KNEE 230
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setgain
|
||||
},
|
||||
[EXPOSURE] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 1023,
|
||||
.step = 1,
|
||||
.default_value = 66,
|
||||
/* 33 ms / 30 fps (except on PASXXX) */
|
||||
#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */
|
||||
.flags = 0,
|
||||
},
|
||||
.set_control = setexposure
|
||||
},
|
||||
/* for coarse exposure */
|
||||
#define COARSE_EXPOSURE_MIN 2
|
||||
#define COARSE_EXPOSURE_MAX 15
|
||||
#define COARSE_EXPOSURE_DEF 2 /* 30 fps */
|
||||
[AUTOGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Automatic Gain (and Exposure)",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
#define AUTOGAIN_DEF 1
|
||||
.default_value = AUTOGAIN_DEF,
|
||||
.flags = V4L2_CTRL_FLAG_UPDATE
|
||||
},
|
||||
.set = sd_setautogain,
|
||||
},
|
||||
[FREQ] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Light frequency filter",
|
||||
.minimum = 0,
|
||||
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
|
||||
.step = 1,
|
||||
#define FREQ_DEF 0
|
||||
.default_value = FREQ_DEF,
|
||||
},
|
||||
.set_control = setfreq
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
|
||||
.bytesperline = 160,
|
||||
|
@ -532,25 +433,27 @@ static const __u8 tas5130_sensor_init[][8] = {
|
|||
};
|
||||
|
||||
static const struct sensor_data sensor_data[] = {
|
||||
SENS(initHv7131d, hv7131d_sensor_init, F_GAIN, NO_BRIGHTNESS|NO_FREQ, 0),
|
||||
SENS(initHv7131r, hv7131r_sensor_init, 0, NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0),
|
||||
SENS(initOv6650, ov6650_sensor_init, F_GAIN|F_SIF, 0, 0x60),
|
||||
SENS(initOv7630, ov7630_sensor_init, F_GAIN, 0, 0x21),
|
||||
SENS(initPas106, pas106_sensor_init, F_GAIN|F_SIF, NO_FREQ, 0),
|
||||
SENS(initPas202, pas202_sensor_init, F_GAIN, NO_FREQ, 0),
|
||||
SENS(initTas5110c, tas5110c_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO,
|
||||
NO_BRIGHTNESS|NO_FREQ, 0),
|
||||
SENS(initTas5110d, tas5110d_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO,
|
||||
NO_BRIGHTNESS|NO_FREQ, 0),
|
||||
SENS(initTas5130, tas5130_sensor_init, F_GAIN,
|
||||
NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0),
|
||||
SENS(initHv7131d, hv7131d_sensor_init, 0, 0),
|
||||
SENS(initHv7131r, hv7131r_sensor_init, 0, 0),
|
||||
SENS(initOv6650, ov6650_sensor_init, F_SIF, 0x60),
|
||||
SENS(initOv7630, ov7630_sensor_init, 0, 0x21),
|
||||
SENS(initPas106, pas106_sensor_init, F_SIF, 0),
|
||||
SENS(initPas202, pas202_sensor_init, 0, 0),
|
||||
SENS(initTas5110c, tas5110c_sensor_init, F_SIF, 0),
|
||||
SENS(initTas5110d, tas5110d_sensor_init, F_SIF, 0),
|
||||
SENS(initTas5130, tas5130_sensor_init, 0, 0),
|
||||
};
|
||||
|
||||
/* get one byte in gspca_dev->usb_buf */
|
||||
static void reg_r(struct gspca_dev *gspca_dev,
|
||||
__u16 value)
|
||||
{
|
||||
usb_control_msg(gspca_dev->dev,
|
||||
int res;
|
||||
|
||||
if (gspca_dev->usb_err < 0)
|
||||
return;
|
||||
|
||||
res = usb_control_msg(gspca_dev->dev,
|
||||
usb_rcvctrlpipe(gspca_dev->dev, 0),
|
||||
0, /* request */
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
|
@ -558,6 +461,12 @@ static void reg_r(struct gspca_dev *gspca_dev,
|
|||
0, /* index */
|
||||
gspca_dev->usb_buf, 1,
|
||||
500);
|
||||
|
||||
if (res < 0) {
|
||||
dev_err(gspca_dev->v4l2_dev.dev,
|
||||
"Error reading register %02x: %d\n", value, res);
|
||||
gspca_dev->usb_err = res;
|
||||
}
|
||||
}
|
||||
|
||||
static void reg_w(struct gspca_dev *gspca_dev,
|
||||
|
@ -565,14 +474,13 @@ static void reg_w(struct gspca_dev *gspca_dev,
|
|||
const __u8 *buffer,
|
||||
int len)
|
||||
{
|
||||
#ifdef GSPCA_DEBUG
|
||||
if (len > USB_BUF_SZ) {
|
||||
PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow");
|
||||
int res;
|
||||
|
||||
if (gspca_dev->usb_err < 0)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
memcpy(gspca_dev->usb_buf, buffer, len);
|
||||
usb_control_msg(gspca_dev->dev,
|
||||
res = usb_control_msg(gspca_dev->dev,
|
||||
usb_sndctrlpipe(gspca_dev->dev, 0),
|
||||
0x08, /* request */
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
|
||||
|
@ -580,30 +488,48 @@ static void reg_w(struct gspca_dev *gspca_dev,
|
|||
0, /* index */
|
||||
gspca_dev->usb_buf, len,
|
||||
500);
|
||||
|
||||
if (res < 0) {
|
||||
dev_err(gspca_dev->v4l2_dev.dev,
|
||||
"Error writing register %02x: %d\n", value, res);
|
||||
gspca_dev->usb_err = res;
|
||||
}
|
||||
}
|
||||
|
||||
static int i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
|
||||
static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
|
||||
{
|
||||
int retry = 60;
|
||||
|
||||
if (gspca_dev->usb_err < 0)
|
||||
return;
|
||||
|
||||
/* is i2c ready */
|
||||
reg_w(gspca_dev, 0x08, buffer, 8);
|
||||
while (retry--) {
|
||||
if (gspca_dev->usb_err < 0)
|
||||
return;
|
||||
msleep(10);
|
||||
reg_r(gspca_dev, 0x08);
|
||||
if (gspca_dev->usb_buf[0] & 0x04) {
|
||||
if (gspca_dev->usb_buf[0] & 0x08)
|
||||
return -1;
|
||||
return 0;
|
||||
if (gspca_dev->usb_buf[0] & 0x08) {
|
||||
dev_err(gspca_dev->v4l2_dev.dev,
|
||||
"i2c write error\n");
|
||||
gspca_dev->usb_err = -EIO;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
dev_err(gspca_dev->v4l2_dev.dev, "i2c write timeout\n");
|
||||
gspca_dev->usb_err = -EIO;
|
||||
}
|
||||
|
||||
static void i2c_w_vector(struct gspca_dev *gspca_dev,
|
||||
const __u8 buffer[][8], int len)
|
||||
{
|
||||
for (;;) {
|
||||
if (gspca_dev->usb_err < 0)
|
||||
return;
|
||||
reg_w(gspca_dev, 0x08, *buffer, 8);
|
||||
len -= 8;
|
||||
if (len <= 0)
|
||||
|
@ -624,11 +550,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
|
||||
/* change reg 0x06 */
|
||||
i2cOV[1] = sensor_data[sd->sensor].sensor_addr;
|
||||
i2cOV[3] = sd->ctrls[BRIGHTNESS].val;
|
||||
if (i2c_w(gspca_dev, i2cOV) < 0)
|
||||
goto err;
|
||||
i2cOV[3] = sd->brightness->val;
|
||||
i2c_w(gspca_dev, i2cOV);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SENSOR_PAS106:
|
||||
case SENSOR_PAS202: {
|
||||
__u8 i2cpbright[] =
|
||||
|
@ -642,54 +567,49 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
i2cpdoit[2] = 0x13;
|
||||
}
|
||||
|
||||
if (sd->ctrls[BRIGHTNESS].val < 127) {
|
||||
if (sd->brightness->val < 127) {
|
||||
/* change reg 0x0b, signreg */
|
||||
i2cpbright[3] = 0x01;
|
||||
/* set reg 0x0c, offset */
|
||||
i2cpbright[4] = 127 - sd->ctrls[BRIGHTNESS].val;
|
||||
i2cpbright[4] = 127 - sd->brightness->val;
|
||||
} else
|
||||
i2cpbright[4] = sd->ctrls[BRIGHTNESS].val - 127;
|
||||
i2cpbright[4] = sd->brightness->val - 127;
|
||||
|
||||
if (i2c_w(gspca_dev, i2cpbright) < 0)
|
||||
goto err;
|
||||
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
||||
goto err;
|
||||
i2c_w(gspca_dev, i2cpbright);
|
||||
i2c_w(gspca_dev, i2cpdoit);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
err:
|
||||
PDEBUG(D_ERR, "i2c error brightness");
|
||||
}
|
||||
|
||||
static void setsensorgain(struct gspca_dev *gspca_dev)
|
||||
static void setgain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 gain = sd->ctrls[GAIN].val;
|
||||
u8 gain = gspca_dev->gain->val;
|
||||
|
||||
switch (sd->sensor) {
|
||||
case SENSOR_HV7131D: {
|
||||
__u8 i2c[] =
|
||||
{0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17};
|
||||
|
||||
i2c[3] = 0x3f - (gain / 4);
|
||||
i2c[4] = 0x3f - (gain / 4);
|
||||
i2c[5] = 0x3f - (gain / 4);
|
||||
i2c[3] = 0x3f - gain;
|
||||
i2c[4] = 0x3f - gain;
|
||||
i2c[5] = 0x3f - gain;
|
||||
|
||||
if (i2c_w(gspca_dev, i2c) < 0)
|
||||
goto err;
|
||||
i2c_w(gspca_dev, i2c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SENSOR_TAS5110C:
|
||||
case SENSOR_TAS5130CXX: {
|
||||
__u8 i2c[] =
|
||||
{0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
|
||||
|
||||
i2c[4] = 255 - gain;
|
||||
if (i2c_w(gspca_dev, i2c) < 0)
|
||||
goto err;
|
||||
i2c_w(gspca_dev, i2c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SENSOR_TAS5110D: {
|
||||
__u8 i2c[] = {
|
||||
0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 };
|
||||
|
@ -703,23 +623,25 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
|
|||
i2c[3] |= (gain & 0x04) << 3;
|
||||
i2c[3] |= (gain & 0x02) << 5;
|
||||
i2c[3] |= (gain & 0x01) << 7;
|
||||
if (i2c_w(gspca_dev, i2c) < 0)
|
||||
goto err;
|
||||
i2c_w(gspca_dev, i2c);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
case SENSOR_OV6650:
|
||||
gain >>= 1;
|
||||
/* fall thru */
|
||||
case SENSOR_OV7630: {
|
||||
__u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
|
||||
|
||||
/*
|
||||
* The ov7630's gain is weird, at 32 the gain drops to the
|
||||
* same level as at 16, so skip 32-47 (of the 0-63 scale).
|
||||
*/
|
||||
if (sd->sensor == SENSOR_OV7630 && gain >= 32)
|
||||
gain += 16;
|
||||
|
||||
i2c[1] = sensor_data[sd->sensor].sensor_addr;
|
||||
i2c[3] = gain >> 2;
|
||||
if (i2c_w(gspca_dev, i2c) < 0)
|
||||
goto err;
|
||||
i2c[3] = gain;
|
||||
i2c_w(gspca_dev, i2c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SENSOR_PAS106:
|
||||
case SENSOR_PAS202: {
|
||||
__u8 i2cpgain[] =
|
||||
|
@ -737,49 +659,27 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
|
|||
i2cpdoit[2] = 0x13;
|
||||
}
|
||||
|
||||
i2cpgain[3] = gain >> 3;
|
||||
i2cpcolorgain[3] = gain >> 4;
|
||||
i2cpcolorgain[4] = gain >> 4;
|
||||
i2cpcolorgain[5] = gain >> 4;
|
||||
i2cpcolorgain[6] = gain >> 4;
|
||||
i2cpgain[3] = gain;
|
||||
i2cpcolorgain[3] = gain >> 1;
|
||||
i2cpcolorgain[4] = gain >> 1;
|
||||
i2cpcolorgain[5] = gain >> 1;
|
||||
i2cpcolorgain[6] = gain >> 1;
|
||||
|
||||
if (i2c_w(gspca_dev, i2cpgain) < 0)
|
||||
goto err;
|
||||
if (i2c_w(gspca_dev, i2cpcolorgain) < 0)
|
||||
goto err;
|
||||
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
||||
goto err;
|
||||
i2c_w(gspca_dev, i2cpgain);
|
||||
i2c_w(gspca_dev, i2cpcolorgain);
|
||||
i2c_w(gspca_dev, i2cpdoit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
err:
|
||||
PDEBUG(D_ERR, "i2c error gain");
|
||||
}
|
||||
|
||||
static void setgain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
__u8 gain;
|
||||
__u8 buf[3] = { 0, 0, 0 };
|
||||
|
||||
if (sensor_data[sd->sensor].flags & F_GAIN) {
|
||||
/* Use the sensor gain to do the actual gain */
|
||||
setsensorgain(gspca_dev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sd->bridge == BRIDGE_103) {
|
||||
gain = sd->ctrls[GAIN].val >> 1;
|
||||
buf[0] = gain; /* Red */
|
||||
buf[1] = gain; /* Green */
|
||||
buf[2] = gain; /* Blue */
|
||||
reg_w(gspca_dev, 0x05, buf, 3);
|
||||
} else {
|
||||
gain = sd->ctrls[GAIN].val >> 4;
|
||||
buf[0] = gain << 4 | gain; /* Red and blue */
|
||||
buf[1] = gain; /* Green */
|
||||
reg_w(gspca_dev, 0x10, buf, 2);
|
||||
default:
|
||||
if (sd->bridge == BRIDGE_103) {
|
||||
u8 buf[3] = { gain, gain, gain }; /* R, G, B */
|
||||
reg_w(gspca_dev, 0x05, buf, 3);
|
||||
} else {
|
||||
u8 buf[2];
|
||||
buf[0] = gain << 4 | gain; /* Red and blue */
|
||||
buf[1] = gain; /* Green */
|
||||
reg_w(gspca_dev, 0x10, buf, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -792,31 +692,24 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
/* Note the datasheet wrongly says line mode exposure uses reg
|
||||
0x26 and 0x27, testing has shown 0x25 + 0x26 */
|
||||
__u8 i2c[] = {0xc0, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x17};
|
||||
/* The HV7131D's exposure goes from 0 - 65535, we scale our
|
||||
exposure of 0-1023 to 0-6138. There are 2 reasons for this:
|
||||
1) This puts our exposure knee of 200 at approx the point
|
||||
where the framerate starts dropping
|
||||
2) At 6138 the framerate has already dropped to 2 fps,
|
||||
going any lower makes little sense */
|
||||
u16 reg = sd->ctrls[EXPOSURE].val * 6;
|
||||
u16 reg = gspca_dev->exposure->val;
|
||||
|
||||
i2c[3] = reg >> 8;
|
||||
i2c[4] = reg & 0xff;
|
||||
if (i2c_w(gspca_dev, i2c) != 0)
|
||||
goto err;
|
||||
i2c_w(gspca_dev, i2c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SENSOR_TAS5110C:
|
||||
case SENSOR_TAS5110D: {
|
||||
/* register 19's high nibble contains the sn9c10x clock divider
|
||||
The high nibble configures the no fps according to the
|
||||
formula: 60 / high_nibble. With a maximum of 30 fps */
|
||||
u8 reg = sd->ctrls[EXPOSURE].val;
|
||||
u8 reg = gspca_dev->exposure->val;
|
||||
|
||||
reg = (reg << 4) | 0x0b;
|
||||
reg_w(gspca_dev, 0x19, ®, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SENSOR_OV6650:
|
||||
case SENSOR_OV7630: {
|
||||
/* The ov6650 / ov7630 have 2 registers which both influence
|
||||
|
@ -848,7 +741,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
} else
|
||||
reg10_max = 0x41;
|
||||
|
||||
reg11 = (15 * sd->ctrls[EXPOSURE].val + 999) / 1000;
|
||||
reg11 = (15 * gspca_dev->exposure->val + 999) / 1000;
|
||||
if (reg11 < 1)
|
||||
reg11 = 1;
|
||||
else if (reg11 > 16)
|
||||
|
@ -861,16 +754,16 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
reg11 = 4;
|
||||
|
||||
/* frame exposure time in ms = 1000 * reg11 / 30 ->
|
||||
reg10 = (sd->ctrls[EXPOSURE].val / 2) * reg10_max
|
||||
reg10 = (gspca_dev->exposure->val / 2) * reg10_max
|
||||
/ (1000 * reg11 / 30) */
|
||||
reg10 = (sd->ctrls[EXPOSURE].val * 15 * reg10_max)
|
||||
reg10 = (gspca_dev->exposure->val * 15 * reg10_max)
|
||||
/ (1000 * reg11);
|
||||
|
||||
/* Don't allow this to get below 10 when using autogain, the
|
||||
steps become very large (relatively) when below 10 causing
|
||||
the image to oscilate from much too dark, to much too bright
|
||||
and back again. */
|
||||
if (sd->ctrls[AUTOGAIN].val && reg10 < 10)
|
||||
if (gspca_dev->autogain->val && reg10 < 10)
|
||||
reg10 = 10;
|
||||
else if (reg10 > reg10_max)
|
||||
reg10 = reg10_max;
|
||||
|
@ -884,12 +777,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
if (sd->reg11 == reg11)
|
||||
i2c[0] = 0xa0;
|
||||
|
||||
if (i2c_w(gspca_dev, i2c) == 0)
|
||||
i2c_w(gspca_dev, i2c);
|
||||
if (gspca_dev->usb_err == 0)
|
||||
sd->reg11 = reg11;
|
||||
else
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SENSOR_PAS202: {
|
||||
__u8 i2cpframerate[] =
|
||||
{0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16};
|
||||
|
@ -909,28 +801,25 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
frame exposure times (like we are doing with the ov chips),
|
||||
as that sometimes leads to jumps in the exposure control,
|
||||
which are bad for auto exposure. */
|
||||
if (sd->ctrls[EXPOSURE].val < 200) {
|
||||
i2cpexpo[3] = 255 - (sd->ctrls[EXPOSURE].val * 255)
|
||||
if (gspca_dev->exposure->val < 200) {
|
||||
i2cpexpo[3] = 255 - (gspca_dev->exposure->val * 255)
|
||||
/ 200;
|
||||
framerate_ctrl = 500;
|
||||
} else {
|
||||
/* The PAS202's exposure control goes from 0 - 4095,
|
||||
but anything below 500 causes vsync issues, so scale
|
||||
our 200-1023 to 500-4095 */
|
||||
framerate_ctrl = (sd->ctrls[EXPOSURE].val - 200)
|
||||
framerate_ctrl = (gspca_dev->exposure->val - 200)
|
||||
* 1000 / 229 + 500;
|
||||
}
|
||||
|
||||
i2cpframerate[3] = framerate_ctrl >> 6;
|
||||
i2cpframerate[4] = framerate_ctrl & 0x3f;
|
||||
if (i2c_w(gspca_dev, i2cpframerate) < 0)
|
||||
goto err;
|
||||
if (i2c_w(gspca_dev, i2cpexpo) < 0)
|
||||
goto err;
|
||||
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
||||
goto err;
|
||||
i2c_w(gspca_dev, i2cpframerate);
|
||||
i2c_w(gspca_dev, i2cpexpo);
|
||||
i2c_w(gspca_dev, i2cpdoit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SENSOR_PAS106: {
|
||||
__u8 i2cpframerate[] =
|
||||
{0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14};
|
||||
|
@ -942,46 +831,40 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
|
||||
/* For values below 150 use partial frame exposure, above
|
||||
that use framerate ctrl */
|
||||
if (sd->ctrls[EXPOSURE].val < 150) {
|
||||
i2cpexpo[3] = 150 - sd->ctrls[EXPOSURE].val;
|
||||
if (gspca_dev->exposure->val < 150) {
|
||||
i2cpexpo[3] = 150 - gspca_dev->exposure->val;
|
||||
framerate_ctrl = 300;
|
||||
} else {
|
||||
/* The PAS106's exposure control goes from 0 - 4095,
|
||||
but anything below 300 causes vsync issues, so scale
|
||||
our 150-1023 to 300-4095 */
|
||||
framerate_ctrl = (sd->ctrls[EXPOSURE].val - 150)
|
||||
framerate_ctrl = (gspca_dev->exposure->val - 150)
|
||||
* 1000 / 230 + 300;
|
||||
}
|
||||
|
||||
i2cpframerate[3] = framerate_ctrl >> 4;
|
||||
i2cpframerate[4] = framerate_ctrl & 0x0f;
|
||||
if (i2c_w(gspca_dev, i2cpframerate) < 0)
|
||||
goto err;
|
||||
if (i2c_w(gspca_dev, i2cpexpo) < 0)
|
||||
goto err;
|
||||
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
||||
goto err;
|
||||
i2c_w(gspca_dev, i2cpframerate);
|
||||
i2c_w(gspca_dev, i2cpexpo);
|
||||
i2c_w(gspca_dev, i2cpdoit);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
err:
|
||||
PDEBUG(D_ERR, "i2c error exposure");
|
||||
}
|
||||
|
||||
static void setfreq(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
switch (sd->sensor) {
|
||||
case SENSOR_OV6650:
|
||||
case SENSOR_OV7630: {
|
||||
if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) {
|
||||
/* Framerate adjust register for artificial light 50 hz flicker
|
||||
compensation, for the ov6650 this is identical to ov6630
|
||||
0x2b register, see ov6630 datasheet.
|
||||
0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */
|
||||
__u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10};
|
||||
switch (sd->ctrls[FREQ].val) {
|
||||
switch (sd->plfreq->val) {
|
||||
default:
|
||||
/* case 0: * no filter*/
|
||||
/* case 2: * 60 hz */
|
||||
|
@ -993,25 +876,17 @@ static void setfreq(struct gspca_dev *gspca_dev)
|
|||
break;
|
||||
}
|
||||
i2c[1] = sensor_data[sd->sensor].sensor_addr;
|
||||
if (i2c_w(gspca_dev, i2c) < 0)
|
||||
PDEBUG(D_ERR, "i2c error setfreq");
|
||||
break;
|
||||
}
|
||||
i2c_w(gspca_dev, i2c);
|
||||
}
|
||||
}
|
||||
|
||||
#define WANT_REGULAR_AUTOGAIN
|
||||
#define WANT_COARSE_EXPO_AUTOGAIN
|
||||
#include "autogain_functions.h"
|
||||
|
||||
static void do_autogain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
int deadzone, desired_avg_lum, result;
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int avg_lum = atomic_read(&sd->avg_lum);
|
||||
int deadzone, desired_avg_lum, avg_lum;
|
||||
|
||||
if ((gspca_dev->ctrl_dis & (1 << AUTOGAIN)) ||
|
||||
avg_lum == -1 || !sd->ctrls[AUTOGAIN].val)
|
||||
avg_lum = atomic_read(&sd->avg_lum);
|
||||
if (avg_lum == -1)
|
||||
return;
|
||||
|
||||
if (sd->autogain_ignore_frames > 0) {
|
||||
|
@ -1030,22 +905,18 @@ static void do_autogain(struct gspca_dev *gspca_dev)
|
|||
desired_avg_lum = 13000;
|
||||
}
|
||||
|
||||
if (sensor_data[sd->sensor].flags & F_COARSE_EXPO)
|
||||
result = coarse_grained_expo_autogain(gspca_dev, avg_lum,
|
||||
sd->ctrls[BRIGHTNESS].val
|
||||
* desired_avg_lum / 127,
|
||||
deadzone);
|
||||
else
|
||||
result = auto_gain_n_exposure(gspca_dev, avg_lum,
|
||||
sd->ctrls[BRIGHTNESS].val
|
||||
* desired_avg_lum / 127,
|
||||
deadzone, GAIN_KNEE, EXPOSURE_KNEE);
|
||||
if (sd->brightness)
|
||||
desired_avg_lum = sd->brightness->val * desired_avg_lum / 127;
|
||||
|
||||
if (result) {
|
||||
PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d",
|
||||
(int) sd->ctrls[GAIN].val,
|
||||
(int) sd->ctrls[EXPOSURE].val);
|
||||
sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
|
||||
if (gspca_dev->exposure->maximum < 500) {
|
||||
if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
|
||||
desired_avg_lum, deadzone))
|
||||
sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
|
||||
} else {
|
||||
int gain_knee = gspca_dev->gain->maximum * 9 / 10;
|
||||
if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum,
|
||||
deadzone, gain_knee, sd->exposure_knee))
|
||||
sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1064,14 +935,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
sd->sensor = id->driver_info >> 8;
|
||||
sd->bridge = id->driver_info & 0xff;
|
||||
|
||||
gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis;
|
||||
#if AUTOGAIN_DEF
|
||||
if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN)))
|
||||
gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
|
||||
#endif
|
||||
|
||||
cam = &gspca_dev->cam;
|
||||
cam->ctrls = sd->ctrls;
|
||||
if (!(sensor_data[sd->sensor].flags & F_SIF)) {
|
||||
cam->cam_mode = vga_mode;
|
||||
cam->nmodes = ARRAY_SIZE(vga_mode);
|
||||
|
@ -1087,18 +951,143 @@ 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)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
const __u8 stop = 0x09; /* Disable stream turn of LED */
|
||||
|
||||
if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) {
|
||||
sd->ctrls[EXPOSURE].min = COARSE_EXPOSURE_MIN;
|
||||
sd->ctrls[EXPOSURE].max = COARSE_EXPOSURE_MAX;
|
||||
sd->ctrls[EXPOSURE].def = COARSE_EXPOSURE_DEF;
|
||||
if (sd->ctrls[EXPOSURE].val > COARSE_EXPOSURE_MAX)
|
||||
sd->ctrls[EXPOSURE].val = COARSE_EXPOSURE_DEF;
|
||||
reg_w(gspca_dev, 0x01, &stop, 1);
|
||||
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
|
||||
/* when switching to autogain set defaults to make sure
|
||||
we are on a valid point of the autogain gain /
|
||||
exposure knee graph, and give this change time to
|
||||
take effect before doing autogain. */
|
||||
gspca_dev->gain->val = gspca_dev->gain->default_value;
|
||||
gspca_dev->exposure->val = gspca_dev->exposure->default_value;
|
||||
sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
|
||||
}
|
||||
|
||||
reg_w(gspca_dev, 0x01, &stop, 1);
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
|
||||
setexposure(gspca_dev);
|
||||
if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
|
||||
setgain(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
setfreq(gspca_dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
/* this function is called at probe time */
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 5);
|
||||
|
||||
if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630 ||
|
||||
sd->sensor == SENSOR_PAS106 || sd->sensor == SENSOR_PAS202)
|
||||
sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
|
||||
|
||||
/* Gain range is sensor dependent */
|
||||
switch (sd->sensor) {
|
||||
case SENSOR_OV6650:
|
||||
case SENSOR_PAS106:
|
||||
case SENSOR_PAS202:
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 31, 1, 15);
|
||||
break;
|
||||
case SENSOR_OV7630:
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 47, 1, 31);
|
||||
break;
|
||||
case SENSOR_HV7131D:
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 63, 1, 31);
|
||||
break;
|
||||
case SENSOR_TAS5110C:
|
||||
case SENSOR_TAS5110D:
|
||||
case SENSOR_TAS5130CXX:
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 255, 1, 127);
|
||||
break;
|
||||
default:
|
||||
if (sd->bridge == BRIDGE_103) {
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 127, 1, 63);
|
||||
} else {
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 15, 1, 7);
|
||||
}
|
||||
}
|
||||
|
||||
/* Exposure range is sensor dependent, and not all have exposure */
|
||||
switch (sd->sensor) {
|
||||
case SENSOR_HV7131D:
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 8191, 1, 482);
|
||||
sd->exposure_knee = 964;
|
||||
break;
|
||||
case SENSOR_OV6650:
|
||||
case SENSOR_OV7630:
|
||||
case SENSOR_PAS106:
|
||||
case SENSOR_PAS202:
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 1023, 1, 66);
|
||||
sd->exposure_knee = 200;
|
||||
break;
|
||||
case SENSOR_TAS5110C:
|
||||
case SENSOR_TAS5110D:
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 2, 15, 1, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
if (gspca_dev->exposure) {
|
||||
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
}
|
||||
|
||||
if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630)
|
||||
sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
|
||||
if (gspca_dev->autogain)
|
||||
v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1242,10 +1231,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
|
||||
sd->frames_to_drop = 0;
|
||||
sd->autogain_ignore_frames = 0;
|
||||
sd->exp_too_high_cnt = 0;
|
||||
sd->exp_too_low_cnt = 0;
|
||||
gspca_dev->exp_too_high_cnt = 0;
|
||||
gspca_dev->exp_too_low_cnt = 0;
|
||||
atomic_set(&sd->avg_lum, -1);
|
||||
return 0;
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static void sd_stopN(struct gspca_dev *gspca_dev)
|
||||
|
@ -1387,37 +1376,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->ctrls[AUTOGAIN].val = val;
|
||||
sd->exp_too_high_cnt = 0;
|
||||
sd->exp_too_low_cnt = 0;
|
||||
|
||||
/* when switching to autogain set defaults to make sure
|
||||
we are on a valid point of the autogain gain /
|
||||
exposure knee graph, and give this change time to
|
||||
take effect before doing autogain. */
|
||||
if (sd->ctrls[AUTOGAIN].val
|
||||
&& !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) {
|
||||
sd->ctrls[EXPOSURE].val = sd->ctrls[EXPOSURE].def;
|
||||
sd->ctrls[GAIN].val = sd->ctrls[GAIN].def;
|
||||
if (gspca_dev->streaming) {
|
||||
sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
|
||||
setexposure(gspca_dev);
|
||||
setgain(gspca_dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (sd->ctrls[AUTOGAIN].val)
|
||||
gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
|
||||
else
|
||||
gspca_dev->ctrl_inac = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_querymenu *menu)
|
||||
{
|
||||
|
@ -1461,10 +1419,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
|
@ -1529,6 +1486,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -3199,6 +3199,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -33,102 +33,11 @@ MODULE_LICENSE("GPL");
|
|||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
u8 brightness;
|
||||
u8 contrast;
|
||||
u8 hue;
|
||||
u8 color;
|
||||
u8 sharpness;
|
||||
|
||||
u8 pkt_seq;
|
||||
|
||||
u8 jpeg_hdr[JPEG_HDR_SZ];
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEF 128
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 8,
|
||||
.step = 1,
|
||||
#define CONTRAST_DEF 1
|
||||
.default_value = CONTRAST_DEF,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_HUE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Hue",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define HUE_DEF 0
|
||||
.default_value = HUE_DEF,
|
||||
},
|
||||
.set = sd_sethue,
|
||||
.get = sd_gethue,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
.maximum = 8,
|
||||
.step = 1,
|
||||
#define COLOR_DEF 1
|
||||
.default_value = COLOR_DEF,
|
||||
},
|
||||
.set = sd_setcolor,
|
||||
.get = sd_getcolor,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SHARPNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Sharpness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define SHARPNESS_DEF 0
|
||||
.default_value = SHARPNESS_DEF,
|
||||
},
|
||||
.set = sd_setsharpness,
|
||||
.get = sd_getsharpness,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
/* (does not work correctly)
|
||||
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
|
||||
|
@ -259,58 +168,40 @@ static void wait_status_1(struct gspca_dev *gspca_dev)
|
|||
gspca_dev->usb_err = -ETIME;
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, sd->brightness);
|
||||
reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, val);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, sd->contrast);
|
||||
reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, val);
|
||||
}
|
||||
|
||||
static void sethue(struct gspca_dev *gspca_dev)
|
||||
static void sethue(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, sd->hue);
|
||||
reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, val);
|
||||
}
|
||||
|
||||
static void setcolor(struct gspca_dev *gspca_dev)
|
||||
static void setcolor(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, sd->color);
|
||||
reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, val);
|
||||
}
|
||||
|
||||
static void setsharpness(struct gspca_dev *gspca_dev)
|
||||
static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, sd->sharpness);
|
||||
reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, val);
|
||||
}
|
||||
|
||||
/* this function is called at probe time */
|
||||
static int sd_config(struct gspca_dev *gspca_dev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
gspca_dev->cam.cam_mode = vga_mode;
|
||||
gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
|
||||
gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */
|
||||
/*fixme: 256 in ms-win traces*/
|
||||
|
||||
sd->brightness = BRIGHTNESS_DEF;
|
||||
sd->contrast = CONTRAST_DEF;
|
||||
sd->hue = HUE_DEF;
|
||||
sd->color = COLOR_DEF;
|
||||
sd->sharpness = SHARPNESS_DEF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -370,14 +261,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
/* the JPEG quality shall be 85% */
|
||||
jpeg_set_qual(sd->jpeg_hdr, 85);
|
||||
|
||||
/* set the controls */
|
||||
setbrightness(gspca_dev);
|
||||
setcontrast(gspca_dev);
|
||||
sethue(gspca_dev);
|
||||
setcolor(gspca_dev);
|
||||
setsharpness(gspca_dev);
|
||||
|
||||
msleep(5);
|
||||
reg_r(gspca_dev, 0x00, 0x2520, 1);
|
||||
msleep(8);
|
||||
|
||||
|
@ -457,103 +340,70 @@ err:
|
|||
gspca_dev->last_packet_type = DISCARD_PACKET;
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
sethue(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolor(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SHARPNESS:
|
||||
setsharpness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 5);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 8, 1, 1);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_HUE, 0, 255, 1, 0);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 8, 1, 1);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SHARPNESS, 0, 255, 1, 0);
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->hue = val;
|
||||
if (gspca_dev->streaming)
|
||||
sethue(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->hue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->color = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcolor(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->color;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->sharpness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setsharpness(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->sharpness;
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.isoc_init = sd_isoc_init,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
|
@ -587,6 +437,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -30,18 +30,12 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
|
|||
MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define QUALITY 85
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
unsigned char brightness;
|
||||
unsigned char contrast;
|
||||
unsigned char colors;
|
||||
u8 quality;
|
||||
#define QUALITY_MIN 70
|
||||
#define QUALITY_MAX 95
|
||||
#define QUALITY_DEF 85
|
||||
|
||||
char subtype;
|
||||
#define AgfaCl20 0
|
||||
#define AiptekPocketDV 1
|
||||
|
@ -62,59 +56,6 @@ struct sd {
|
|||
u8 jpeg_hdr[JPEG_HDR_SZ];
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEF 127
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 63,
|
||||
.step = 1,
|
||||
#define CONTRAST_DEF 31
|
||||
.default_value = CONTRAST_DEF,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Color",
|
||||
.minimum = 0,
|
||||
.maximum = 63,
|
||||
.step = 1,
|
||||
#define COLOR_DEF 31
|
||||
.default_value = COLOR_DEF,
|
||||
},
|
||||
.set = sd_setcolors,
|
||||
.get = sd_getcolors,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
|
||||
.bytesperline = 320,
|
||||
|
@ -641,10 +582,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->cam_mode = sif_mode;
|
||||
cam->nmodes = ARRAY_SIZE(sif_mode);
|
||||
}
|
||||
sd->brightness = BRIGHTNESS_DEF;
|
||||
sd->contrast = CONTRAST_DEF;
|
||||
sd->colors = COLOR_DEF;
|
||||
sd->quality = QUALITY_DEF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -673,7 +610,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
/* create the JPEG header */
|
||||
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
|
||||
0x22); /* JPEG 411 */
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
jpeg_set_qual(sd->jpeg_hdr, QUALITY);
|
||||
|
||||
if (sd->subtype == LogitechClickSmart310) {
|
||||
xmult = 0x16;
|
||||
|
@ -934,122 +871,79 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_w(gspca_dev, 0x00, 0x8167,
|
||||
(__u8) (sd->brightness - 128));
|
||||
(__u8) (val - 128));
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_w(gspca_dev, 0x00, 0x8168, sd->contrast);
|
||||
reg_w(gspca_dev, 0x00, 0x8168, val);
|
||||
}
|
||||
|
||||
static void setcolors(struct gspca_dev *gspca_dev)
|
||||
static void setcolors(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_w(gspca_dev, 0x00, 0x8169, sd->colors);
|
||||
reg_w(gspca_dev, 0x00, 0x8169, val);
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolors(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 3);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 63, 1, 31);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 63, 1, 31);
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->colors = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcolors(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->colors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (jcomp->quality < QUALITY_MIN)
|
||||
sd->quality = QUALITY_MIN;
|
||||
else if (jcomp->quality > QUALITY_MAX)
|
||||
sd->quality = QUALITY_MAX;
|
||||
else
|
||||
sd->quality = jcomp->quality;
|
||||
if (gspca_dev->streaming)
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
memset(jcomp, 0, sizeof *jcomp);
|
||||
jcomp->quality = sd->quality;
|
||||
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
||||
| V4L2_JPEG_MARKER_DQT;
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.get_jcomp = sd_get_jcomp,
|
||||
.set_jcomp = sd_set_jcomp,
|
||||
};
|
||||
|
||||
/* -- module initialisation -- */
|
||||
|
@ -1089,6 +983,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -49,91 +49,6 @@ struct sd {
|
|||
#define ViewQuestM318B 6
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
#define MY_BRIGHTNESS 0
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
#define MY_CONTRAST 1
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 64725,
|
||||
.step = 1,
|
||||
.default_value = 64725,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
#define MY_COLOR 2
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Color",
|
||||
.minimum = 0,
|
||||
.maximum = 63,
|
||||
.step = 1,
|
||||
.default_value = 20,
|
||||
},
|
||||
.set = sd_setcolors,
|
||||
.get = sd_getcolors,
|
||||
},
|
||||
#define MY_BLUE_BALANCE 3
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BLUE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Blue Balance",
|
||||
.minimum = 0,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set = sd_setblue_balance,
|
||||
.get = sd_getblue_balance,
|
||||
},
|
||||
#define MY_RED_BALANCE 4
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_RED_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Red Balance",
|
||||
.minimum = 0,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set = sd_setred_balance,
|
||||
.get = sd_getred_balance,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
|
||||
.bytesperline = 160,
|
||||
|
@ -1878,42 +1793,32 @@ static int write_vector(struct gspca_dev *gspca_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness);
|
||||
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, val);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_write(gspca_dev->dev, 0x00, 0x00,
|
||||
(sd->contrast >> 8) & 0xff);
|
||||
(val >> 8) & 0xff);
|
||||
reg_write(gspca_dev->dev, 0x00, 0x01,
|
||||
sd->contrast & 0xff);
|
||||
val & 0xff);
|
||||
}
|
||||
|
||||
static void setcolors(struct gspca_dev *gspca_dev)
|
||||
static void setcolors(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors);
|
||||
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, val);
|
||||
}
|
||||
|
||||
static void setblue_balance(struct gspca_dev *gspca_dev)
|
||||
static void setblue_balance(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance);
|
||||
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, val);
|
||||
}
|
||||
|
||||
static void setred_balance(struct gspca_dev *gspca_dev)
|
||||
static void setred_balance(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance);
|
||||
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, val);
|
||||
}
|
||||
|
||||
/* this function is called at probe time */
|
||||
|
@ -1927,9 +1832,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->cam_mode = vga_mode;
|
||||
cam->nmodes = ARRAY_SIZE(vga_mode);
|
||||
sd->subtype = id->driver_info;
|
||||
sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value;
|
||||
sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value;
|
||||
sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2008,13 +1910,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02);
|
||||
|
||||
/* HDG atleast the Intel CreateAndShare needs to have one of its
|
||||
* brightness / contrast / color set otherwise it assumes what seems
|
||||
* max contrast. Note that strange enough setting any of these is
|
||||
* enough to fix the max contrast problem, to be sure we set all 3 */
|
||||
setbrightness(gspca_dev);
|
||||
setcontrast(gspca_dev);
|
||||
setcolors(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2053,103 +1948,70 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolors(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
setblue_balance(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
setred_balance(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 5);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 127, 1, 0);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 64725, 1, 64725);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 63, 1, 20);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_RED_BALANCE, 0, 127, 1, 0);
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->colors = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcolors(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->colors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->blue_balance = val;
|
||||
if (gspca_dev->streaming)
|
||||
setblue_balance(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->blue_balance;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->red_balance = val;
|
||||
if (gspca_dev->streaming)
|
||||
setred_balance(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->red_balance;
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.stop0 = sd_stop0,
|
||||
|
@ -2185,6 +2047,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -33,34 +33,11 @@ MODULE_LICENSE("GPL");
|
|||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
u8 brightness;
|
||||
|
||||
u8 subtype;
|
||||
#define IntelPCCameraPro 0
|
||||
#define Nxultra 1
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEF 127
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
|
||||
.bytesperline = 160,
|
||||
|
@ -633,7 +610,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->nmodes = ARRAY_SIZE(vga_mode);
|
||||
else /* no 640x480 for IntelPCCameraPro */
|
||||
cam->nmodes = ARRAY_SIZE(vga_mode) - 1;
|
||||
sd->brightness = BRIGHTNESS_DEF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -651,11 +627,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 brightness = sd->brightness;
|
||||
|
||||
reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6);
|
||||
reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2);
|
||||
}
|
||||
|
@ -706,13 +679,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]);
|
||||
reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]);
|
||||
|
||||
ret = reg_write(dev, SPCA50X_REG_USB,
|
||||
return reg_write(dev, SPCA50X_REG_USB,
|
||||
SPCA50X_USB_CTRL,
|
||||
SPCA50X_CUSB_ENABLE);
|
||||
|
||||
setbrightness(gspca_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sd_stopN(struct gspca_dev *gspca_dev)
|
||||
|
@ -756,30 +725,49 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
*val = sd->brightness;
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 5);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init_controls = sd_init_controls,
|
||||
.init = sd_init,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
|
@ -812,6 +800,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -33,83 +33,10 @@ MODULE_LICENSE("GPL");
|
|||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
unsigned char brightness;
|
||||
unsigned char contrast;
|
||||
unsigned char colors;
|
||||
unsigned char hue;
|
||||
char norme;
|
||||
char channel;
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
#define SD_BRIGHTNESS 0
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = 0x80,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
#define SD_CONTRAST 1
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = 0x47,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
#define SD_COLOR 2
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = 0x40,
|
||||
},
|
||||
.set = sd_setcolors,
|
||||
.get = sd_getcolors,
|
||||
},
|
||||
#define SD_HUE 3
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_HUE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Hue",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
.set = sd_sethue,
|
||||
.get = sd_gethue,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
|
||||
.bytesperline = 160,
|
||||
|
@ -281,16 +208,11 @@ static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
|
|||
static int sd_config(struct gspca_dev *gspca_dev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct cam *cam;
|
||||
|
||||
cam = &gspca_dev->cam;
|
||||
cam->cam_mode = vga_mode;
|
||||
cam->nmodes = ARRAY_SIZE(vga_mode);
|
||||
sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
|
||||
sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
|
||||
sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
|
||||
sd->hue = sd_ctrls[SD_HUE].qctrl.default_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -564,121 +486,93 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
spca506_Initi2c(gspca_dev);
|
||||
spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright);
|
||||
spca506_WriteI2c(gspca_dev, val, SAA7113_bright);
|
||||
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
spca506_Initi2c(gspca_dev);
|
||||
spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast);
|
||||
spca506_WriteI2c(gspca_dev, val, SAA7113_contrast);
|
||||
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
|
||||
}
|
||||
|
||||
static void setcolors(struct gspca_dev *gspca_dev)
|
||||
static void setcolors(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
spca506_Initi2c(gspca_dev);
|
||||
spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation);
|
||||
spca506_WriteI2c(gspca_dev, val, SAA7113_saturation);
|
||||
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
|
||||
}
|
||||
|
||||
static void sethue(struct gspca_dev *gspca_dev)
|
||||
static void sethue(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
spca506_Initi2c(gspca_dev);
|
||||
spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue);
|
||||
spca506_WriteI2c(gspca_dev, val, SAA7113_hue);
|
||||
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolors(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
sethue(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 4);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 255, 1, 0x47);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 255, 1, 0x40);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_HUE, 0, 255, 1, 0);
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->colors = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcolors(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->colors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->hue = val;
|
||||
if (gspca_dev->streaming)
|
||||
sethue(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->hue;
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
|
@ -711,6 +605,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -32,8 +32,6 @@ MODULE_LICENSE("GPL");
|
|||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
u8 brightness;
|
||||
|
||||
u8 subtype;
|
||||
#define CreativeVista 0
|
||||
#define HamaUSBSightcam 1
|
||||
|
@ -43,27 +41,6 @@ struct sd {
|
|||
#define ViewQuestVQ110 5
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEF 128
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format sif_mode[] = {
|
||||
{160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
|
||||
.bytesperline = 160,
|
||||
|
@ -1411,7 +1388,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->nmodes = ARRAY_SIZE(sif_mode);
|
||||
|
||||
sd->subtype = id->driver_info;
|
||||
sd->brightness = BRIGHTNESS_DEF;
|
||||
|
||||
init_data = init_data_tb[sd->subtype];
|
||||
return write_vector(gspca_dev, init_data);
|
||||
|
@ -1471,11 +1447,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 brightness = sd->brightness;
|
||||
|
||||
/* MX seem contrast */
|
||||
reg_write(gspca_dev->dev, 0x8651, brightness);
|
||||
reg_write(gspca_dev->dev, 0x8652, brightness);
|
||||
|
@ -1483,31 +1456,50 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
|||
reg_write(gspca_dev->dev, 0x8654, brightness);
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
*val = sd->brightness;
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 5);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
|
@ -1541,6 +1533,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -31,39 +31,17 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
|
|||
MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define EXPOSURE_MAX (2047 + 325)
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
__u16 exposure; /* rev12a only */
|
||||
#define EXPOSURE_MIN 1
|
||||
#define EXPOSURE_DEF 700 /* == 10 fps */
|
||||
#define EXPOSURE_MAX (2047 + 325) /* see setexposure */
|
||||
|
||||
__u8 contrast; /* rev72a only */
|
||||
#define CONTRAST_MIN 0x00
|
||||
#define CONTRAST_DEF 0x20
|
||||
#define CONTRAST_MAX 0x3f
|
||||
|
||||
__u8 brightness; /* rev72a only */
|
||||
#define BRIGHTNESS_MIN 0
|
||||
#define BRIGHTNESS_DEF 0x20
|
||||
#define BRIGHTNESS_MAX 0x3f
|
||||
|
||||
__u8 white;
|
||||
#define HUE_MIN 1
|
||||
#define HUE_DEF 0x40
|
||||
#define HUE_MAX 0x7f
|
||||
|
||||
__u8 autogain;
|
||||
#define AUTOGAIN_MIN 0
|
||||
#define AUTOGAIN_DEF 1
|
||||
#define AUTOGAIN_MAX 1
|
||||
|
||||
__u8 gain; /* rev12a only */
|
||||
#define GAIN_MIN 0
|
||||
#define GAIN_DEF 63
|
||||
#define GAIN_MAX 255
|
||||
struct { /* hue/contrast control cluster */
|
||||
struct v4l2_ctrl *contrast;
|
||||
struct v4l2_ctrl *hue;
|
||||
};
|
||||
struct v4l2_ctrl *autogain;
|
||||
|
||||
#define EXPO12A_DEF 3
|
||||
__u8 expo12a; /* expo/gain? for rev 12a */
|
||||
|
@ -461,12 +439,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->cam_mode = sif_072a_mode;
|
||||
cam->nmodes = ARRAY_SIZE(sif_072a_mode);
|
||||
}
|
||||
sd->brightness = BRIGHTNESS_DEF;
|
||||
sd->contrast = CONTRAST_DEF;
|
||||
sd->white = HUE_DEF;
|
||||
sd->exposure = EXPOSURE_DEF;
|
||||
sd->autogain = AUTOGAIN_DEF;
|
||||
sd->gain = GAIN_DEF;
|
||||
sd->expo12a = EXPO12A_DEF;
|
||||
return 0;
|
||||
}
|
||||
|
@ -491,66 +463,49 @@ static int sd_init_72a(struct gspca_dev *gspca_dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* rev 72a only */
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct usb_device *dev = gspca_dev->dev;
|
||||
__u8 value;
|
||||
__u16 reg;
|
||||
|
||||
value = sd->brightness;
|
||||
if (sd->chip_revision == Rev012A)
|
||||
reg = 0x8610;
|
||||
else
|
||||
reg = 0x8611;
|
||||
|
||||
/* offsets for white balance */
|
||||
reg_w_val(dev, 0x8611, value); /* R */
|
||||
reg_w_val(dev, 0x8612, value); /* Gr */
|
||||
reg_w_val(dev, 0x8613, value); /* B */
|
||||
reg_w_val(dev, 0x8614, value); /* Gb */
|
||||
reg_w_val(dev, reg + 0, val); /* R */
|
||||
reg_w_val(dev, reg + 1, val); /* Gr */
|
||||
reg_w_val(dev, reg + 2, val); /* B */
|
||||
reg_w_val(dev, reg + 3, val); /* Gb */
|
||||
}
|
||||
|
||||
static void setwhite(struct gspca_dev *gspca_dev)
|
||||
static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
__u16 white;
|
||||
struct usb_device *dev = gspca_dev->dev;
|
||||
__u8 blue, red;
|
||||
__u16 reg;
|
||||
|
||||
/* try to emulate MS-win as possible */
|
||||
white = sd->white;
|
||||
red = 0x20 + white * 3 / 8;
|
||||
blue = 0x90 - white * 5 / 8;
|
||||
if (sd->chip_revision == Rev012A) {
|
||||
reg = 0x8614;
|
||||
} else {
|
||||
reg = 0x8651;
|
||||
red += sd->contrast - 0x20;
|
||||
blue += sd->contrast - 0x20;
|
||||
red += contrast - 0x20;
|
||||
blue += contrast - 0x20;
|
||||
reg_w_val(dev, 0x8652, contrast + 0x20); /* Gr */
|
||||
reg_w_val(dev, 0x8654, contrast + 0x20); /* Gb */
|
||||
}
|
||||
reg_w_val(gspca_dev->dev, reg, red);
|
||||
reg_w_val(gspca_dev->dev, reg + 2, blue);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct usb_device *dev = gspca_dev->dev;
|
||||
__u8 value;
|
||||
|
||||
if (sd->chip_revision != Rev072A)
|
||||
return;
|
||||
value = sd->contrast + 0x20;
|
||||
|
||||
/* gains for white balance */
|
||||
setwhite(gspca_dev);
|
||||
/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */
|
||||
reg_w_val(dev, 0x8652, value); /* Gr */
|
||||
/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */
|
||||
reg_w_val(dev, 0x8654, value); /* Gb */
|
||||
reg_w_val(dev, reg, red);
|
||||
reg_w_val(dev, reg + 2, blue);
|
||||
}
|
||||
|
||||
/* rev 12a only */
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int i, expo = 0;
|
||||
|
||||
/* Register 0x8309 controls exposure for the spca561,
|
||||
|
@ -572,8 +527,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
int table[] = { 0, 450, 550, 625, EXPOSURE_MAX };
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
|
||||
if (sd->exposure <= table[i + 1]) {
|
||||
expo = sd->exposure - table[i];
|
||||
if (val <= table[i + 1]) {
|
||||
expo = val - table[i];
|
||||
if (i)
|
||||
expo += 300;
|
||||
expo |= i << 11;
|
||||
|
@ -587,29 +542,27 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
|
||||
/* rev 12a only */
|
||||
static void setgain(struct gspca_dev *gspca_dev)
|
||||
static void setgain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
/* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the
|
||||
sensitivity when set, so 31 + one of them set == 63, and 15
|
||||
with both of them set == 63 */
|
||||
if (sd->gain < 64)
|
||||
gspca_dev->usb_buf[0] = sd->gain;
|
||||
else if (sd->gain < 128)
|
||||
gspca_dev->usb_buf[0] = (sd->gain / 2) | 0x40;
|
||||
if (val < 64)
|
||||
gspca_dev->usb_buf[0] = val;
|
||||
else if (val < 128)
|
||||
gspca_dev->usb_buf[0] = (val / 2) | 0x40;
|
||||
else
|
||||
gspca_dev->usb_buf[0] = (sd->gain / 4) | 0xc0;
|
||||
gspca_dev->usb_buf[0] = (val / 4) | 0xc0;
|
||||
|
||||
gspca_dev->usb_buf[1] = 0;
|
||||
reg_w_buf(gspca_dev, 0x8335, 2);
|
||||
}
|
||||
|
||||
static void setautogain(struct gspca_dev *gspca_dev)
|
||||
static void setautogain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->autogain)
|
||||
if (val)
|
||||
sd->ag_cnt = AG_CNT_START;
|
||||
else
|
||||
sd->ag_cnt = -1;
|
||||
|
@ -644,9 +597,6 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
|
|||
memcpy(gspca_dev->usb_buf, Reg8391, 8);
|
||||
reg_w_buf(gspca_dev, 0x8391, 8);
|
||||
reg_w_buf(gspca_dev, 0x8390, 8);
|
||||
setwhite(gspca_dev);
|
||||
setgain(gspca_dev);
|
||||
setexposure(gspca_dev);
|
||||
|
||||
/* Led ON (bit 3 -> 0 */
|
||||
reg_w_val(gspca_dev->dev, 0x8114, 0x00);
|
||||
|
@ -654,6 +604,7 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
static int sd_start_72a(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct usb_device *dev = gspca_dev->dev;
|
||||
int Clck;
|
||||
int mode;
|
||||
|
@ -683,9 +634,10 @@ static int sd_start_72a(struct gspca_dev *gspca_dev)
|
|||
reg_w_val(dev, 0x8702, 0x81);
|
||||
reg_w_val(dev, 0x8500, mode); /* mode */
|
||||
write_sensor_72a(gspca_dev, rev72a_init_sensor2);
|
||||
setcontrast(gspca_dev);
|
||||
setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue),
|
||||
v4l2_ctrl_g_ctrl(sd->contrast));
|
||||
/* setbrightness(gspca_dev); * fixme: bad values */
|
||||
setautogain(gspca_dev);
|
||||
setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
|
||||
reg_w_val(dev, 0x8112, 0x10 | 0x20);
|
||||
return 0;
|
||||
}
|
||||
|
@ -819,221 +771,96 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
||||
}
|
||||
|
||||
/* rev 72a only */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
return 0;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
/* hue/contrast control cluster for 72a */
|
||||
setwhite(gspca_dev, sd->hue->val, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
/* just plain hue control for 12a */
|
||||
setwhite(gspca_dev, ctrl->val, 0);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
setexposure(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
setgain(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
setautogain(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rev 72a only */
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->autogain = val;
|
||||
if (gspca_dev->streaming)
|
||||
setautogain(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->autogain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->white = val;
|
||||
if (gspca_dev->streaming)
|
||||
setwhite(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getwhite(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->white;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rev12a only */
|
||||
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->exposure = val;
|
||||
if (gspca_dev->streaming)
|
||||
setexposure(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->exposure;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rev12a only */
|
||||
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->gain = val;
|
||||
if (gspca_dev->streaming)
|
||||
setgain(gspca_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->gain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* control tables */
|
||||
static const struct ctrl sd_ctrls_12a[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_HUE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Hue",
|
||||
.minimum = HUE_MIN,
|
||||
.maximum = HUE_MAX,
|
||||
.step = 1,
|
||||
.default_value = HUE_DEF,
|
||||
},
|
||||
.set = sd_setwhite,
|
||||
.get = sd_getwhite,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = EXPOSURE_MIN,
|
||||
.maximum = EXPOSURE_MAX,
|
||||
.step = 1,
|
||||
.default_value = EXPOSURE_DEF,
|
||||
},
|
||||
.set = sd_setexposure,
|
||||
.get = sd_getexposure,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = GAIN_MIN,
|
||||
.maximum = GAIN_MAX,
|
||||
.step = 1,
|
||||
.default_value = GAIN_DEF,
|
||||
},
|
||||
.set = sd_setgain,
|
||||
.get = sd_getgain,
|
||||
},
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static const struct ctrl sd_ctrls_72a[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_HUE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Hue",
|
||||
.minimum = HUE_MIN,
|
||||
.maximum = HUE_MAX,
|
||||
.step = 1,
|
||||
.default_value = HUE_DEF,
|
||||
},
|
||||
.set = sd_setwhite,
|
||||
.get = sd_getwhite,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = BRIGHTNESS_MIN,
|
||||
.maximum = BRIGHTNESS_MAX,
|
||||
.step = 1,
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = CONTRAST_MIN,
|
||||
.maximum = CONTRAST_MAX,
|
||||
.step = 1,
|
||||
.default_value = CONTRAST_DEF,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Gain",
|
||||
.minimum = AUTOGAIN_MIN,
|
||||
.maximum = AUTOGAIN_MAX,
|
||||
.step = 1,
|
||||
.default_value = AUTOGAIN_DEF,
|
||||
},
|
||||
.set = sd_setautogain,
|
||||
.get = sd_getautogain,
|
||||
},
|
||||
};
|
||||
static int sd_init_controls_12a(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 3);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 255, 1, 63);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_init_controls_72a(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 4);
|
||||
sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20);
|
||||
sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20);
|
||||
sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
v4l2_ctrl_cluster(2, &sd->contrast);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc_12a = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls_12a,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls_12a),
|
||||
.init_controls = sd_init_controls_12a,
|
||||
.config = sd_config,
|
||||
.init = sd_init_12a,
|
||||
.start = sd_start_12a,
|
||||
|
@ -1045,8 +872,7 @@ static const struct sd_desc sd_desc_12a = {
|
|||
};
|
||||
static const struct sd_desc sd_desc_72a = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls_72a,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls_72a),
|
||||
.init_controls = sd_init_controls_72a,
|
||||
.config = sd_config,
|
||||
.init = sd_init_72a,
|
||||
.start = sd_start_72a,
|
||||
|
@ -1103,6 +929,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -433,6 +433,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -340,6 +340,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -36,8 +36,10 @@ MODULE_LICENSE("GPL");
|
|||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
u16 expo;
|
||||
u8 gain;
|
||||
struct { /* exposure/gain control cluster */
|
||||
struct v4l2_ctrl *exposure;
|
||||
struct v4l2_ctrl *gain;
|
||||
};
|
||||
|
||||
u8 do_ctrl;
|
||||
u8 gpio[2];
|
||||
|
@ -55,42 +57,6 @@ enum sensors {
|
|||
SENSOR_OV9630,
|
||||
};
|
||||
|
||||
static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0x0001,
|
||||
.maximum = 0x0fff,
|
||||
.step = 1,
|
||||
#define EXPO_DEF 0x0356
|
||||
.default_value = EXPO_DEF,
|
||||
},
|
||||
.set = sd_setexpo,
|
||||
.get = sd_getexpo,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0x01,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
#define GAIN_DEF 0x8d
|
||||
.default_value = GAIN_DEF,
|
||||
},
|
||||
.set = sd_setgain,
|
||||
.get = sd_getgain,
|
||||
},
|
||||
};
|
||||
|
||||
static struct v4l2_pix_format vga_mode[] = {
|
||||
{320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
|
||||
.bytesperline = 320,
|
||||
|
@ -791,7 +757,7 @@ static void lz24bp_ppl(struct sd *sd, u16 ppl)
|
|||
ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
|
||||
}
|
||||
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int i, integclks, intstartclk, frameclks, min_frclk;
|
||||
|
@ -799,7 +765,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
u16 cmd;
|
||||
u8 buf[15];
|
||||
|
||||
integclks = sd->expo;
|
||||
integclks = expo;
|
||||
i = 0;
|
||||
cmd = SQ930_CTRL_SET_EXPOSURE;
|
||||
|
||||
|
@ -818,7 +784,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
buf[i++] = intstartclk;
|
||||
buf[i++] = frameclks >> 8;
|
||||
buf[i++] = frameclks;
|
||||
buf[i++] = sd->gain;
|
||||
buf[i++] = gain;
|
||||
break;
|
||||
default: /* cmos */
|
||||
/* case SENSOR_MI0360: */
|
||||
|
@ -834,7 +800,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
buf[i++] = 0x35; /* reg = global gain */
|
||||
buf[i++] = 0x00; /* val H */
|
||||
buf[i++] = sensor->i2c_dum;
|
||||
buf[i++] = 0x80 + sd->gain / 2; /* val L */
|
||||
buf[i++] = 0x80 + gain / 2; /* val L */
|
||||
buf[i++] = 0x00;
|
||||
buf[i++] = 0x00;
|
||||
buf[i++] = 0x00;
|
||||
|
@ -860,9 +826,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
|
||||
cam->bulk = 1;
|
||||
|
||||
sd->gain = GAIN_DEF;
|
||||
sd->expo = EXPO_DEF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1089,7 +1052,8 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
|
|||
return;
|
||||
sd->do_ctrl = 0;
|
||||
|
||||
setexposure(gspca_dev);
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure),
|
||||
v4l2_ctrl_g_ctrl(sd->gain));
|
||||
|
||||
gspca_dev->cam.bulk_nurbs = 1;
|
||||
ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
|
||||
|
@ -1113,48 +1077,55 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
|
||||
}
|
||||
|
||||
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->gain = val;
|
||||
if (gspca_dev->streaming)
|
||||
sd->do_ctrl = 1;
|
||||
return 0;
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_EXPOSURE:
|
||||
setexposure(gspca_dev, ctrl->val, sd->gain->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->gain;
|
||||
return 0;
|
||||
}
|
||||
static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 2);
|
||||
sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356);
|
||||
sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 1, 255, 1, 0x8d);
|
||||
|
||||
sd->expo = val;
|
||||
if (gspca_dev->streaming)
|
||||
sd->do_ctrl = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->expo;
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
v4l2_ctrl_cluster(2, &sd->exposure);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.isoc_init = sd_isoc_init,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
|
@ -1194,6 +1165,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -29,86 +29,14 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
|
|||
MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* controls */
|
||||
enum e_ctrl {
|
||||
BRIGHTNESS,
|
||||
CONTRAST,
|
||||
COLORS,
|
||||
LIGHTFREQ,
|
||||
NCTRLS /* number of controls */
|
||||
};
|
||||
#define QUALITY 50
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
struct gspca_ctrl ctrls[NCTRLS];
|
||||
|
||||
u8 quality;
|
||||
#define QUALITY_MIN 70
|
||||
#define QUALITY_MAX 95
|
||||
#define QUALITY_DEF 88
|
||||
|
||||
u8 jpeg_hdr[JPEG_HDR_SZ];
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static void setbrightness(struct gspca_dev *gspca_dev);
|
||||
static void setcontrast(struct gspca_dev *gspca_dev);
|
||||
static void setcolors(struct gspca_dev *gspca_dev);
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev);
|
||||
|
||||
static const struct ctrl sd_ctrls[NCTRLS] = {
|
||||
[BRIGHTNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setbrightness
|
||||
},
|
||||
[CONTRAST] = {
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setcontrast
|
||||
},
|
||||
[COLORS] = {
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Color",
|
||||
.minimum = 0,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 127,
|
||||
},
|
||||
.set_control = setcolors
|
||||
},
|
||||
[LIGHTFREQ] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.type = V4L2_CTRL_TYPE_MENU,
|
||||
.name = "Light frequency filter",
|
||||
.minimum = 1,
|
||||
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
.set_control = setlightfreq
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
|
||||
.bytesperline = 320,
|
||||
|
@ -255,41 +183,36 @@ static void set_par(struct gspca_dev *gspca_dev,
|
|||
snd_val(gspca_dev, 0x003f08, parval);
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int parval;
|
||||
|
||||
parval = 0x06000000 /* whiteness */
|
||||
+ (sd->ctrls[BRIGHTNESS].val << 16);
|
||||
+ (val << 16);
|
||||
set_par(gspca_dev, parval);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int parval;
|
||||
|
||||
parval = 0x07000000 /* contrast */
|
||||
+ (sd->ctrls[CONTRAST].val << 16);
|
||||
+ (val << 16);
|
||||
set_par(gspca_dev, parval);
|
||||
}
|
||||
|
||||
static void setcolors(struct gspca_dev *gspca_dev)
|
||||
static void setcolors(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int parval;
|
||||
|
||||
parval = 0x08000000 /* saturation */
|
||||
+ (sd->ctrls[COLORS].val << 16);
|
||||
+ (val << 16);
|
||||
set_par(gspca_dev, parval);
|
||||
}
|
||||
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev)
|
||||
static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
set_par(gspca_dev, sd->ctrls[LIGHTFREQ].val == 1
|
||||
set_par(gspca_dev, val == 1
|
||||
? 0x33640000 /* 50 Hz */
|
||||
: 0x33780000); /* 60 Hz */
|
||||
}
|
||||
|
@ -298,12 +221,8 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
|
|||
static int sd_config(struct gspca_dev *gspca_dev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
gspca_dev->cam.cam_mode = vga_mode;
|
||||
gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
|
||||
gspca_dev->cam.ctrls = sd->ctrls;
|
||||
sd->quality = QUALITY_DEF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -333,7 +252,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
/* create the JPEG header */
|
||||
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
|
||||
0x22); /* JPEG 411 */
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
jpeg_set_qual(sd->jpeg_hdr, QUALITY);
|
||||
|
||||
/* work on alternate 1 */
|
||||
usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
|
||||
|
@ -365,14 +284,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x0640, 0);
|
||||
reg_w(gspca_dev, 0x0650, 0);
|
||||
reg_w(gspca_dev, 0x0660, 0);
|
||||
setbrightness(gspca_dev); /* whiteness */
|
||||
setcontrast(gspca_dev); /* contrast */
|
||||
setcolors(gspca_dev); /* saturation */
|
||||
set_par(gspca_dev, 0x09800000); /* Red ? */
|
||||
set_par(gspca_dev, 0x0a800000); /* Green ? */
|
||||
set_par(gspca_dev, 0x0b800000); /* Blue ? */
|
||||
set_par(gspca_dev, 0x0d030000); /* Gamma ? */
|
||||
setlightfreq(gspca_dev);
|
||||
|
||||
/* start the video flow */
|
||||
set_par(gspca_dev, 0x01000000);
|
||||
|
@ -435,62 +350,70 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
||||
}
|
||||
|
||||
static int sd_querymenu(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_querymenu *menu)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
|
||||
switch (menu->id) {
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm))
|
||||
break;
|
||||
strcpy((char *) menu->name, freq_nm[menu->index]);
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolors(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_POWER_LINE_FREQUENCY:
|
||||
setlightfreq(gspca_dev, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (jcomp->quality < QUALITY_MIN)
|
||||
sd->quality = QUALITY_MIN;
|
||||
else if (jcomp->quality > QUALITY_MAX)
|
||||
sd->quality = QUALITY_MAX;
|
||||
else
|
||||
sd->quality = jcomp->quality;
|
||||
if (gspca_dev->streaming)
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
memset(jcomp, 0, sizeof *jcomp);
|
||||
jcomp->quality = sd->quality;
|
||||
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
||||
| V4L2_JPEG_MARKER_DQT;
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 4);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 255, 1, 127);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 255, 1, 127);
|
||||
v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = NCTRLS,
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.querymenu = sd_querymenu,
|
||||
.get_jcomp = sd_get_jcomp,
|
||||
.set_jcomp = sd_set_jcomp,
|
||||
};
|
||||
|
||||
/* -- module initialisation -- */
|
||||
|
@ -516,6 +439,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -46,10 +46,6 @@ struct sd {
|
|||
u8 current_mode;
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
};
|
||||
|
||||
static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
|
||||
int size)
|
||||
{
|
||||
|
@ -318,8 +314,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.start = sd_start,
|
||||
|
@ -352,6 +346,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -30,18 +30,13 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
|
|||
MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define QUALITY 85
|
||||
|
||||
/* specific webcam descriptor */
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
s8 brightness;
|
||||
u8 contrast;
|
||||
u8 colors;
|
||||
u8 autogain;
|
||||
u8 quality;
|
||||
#define QUALITY_MIN 70
|
||||
#define QUALITY_MAX 95
|
||||
#define QUALITY_DEF 85
|
||||
bool autogain;
|
||||
|
||||
u8 bridge;
|
||||
#define BRIDGE_SPCA504 0
|
||||
|
@ -59,75 +54,6 @@ struct sd {
|
|||
u8 jpeg_hdr[JPEG_HDR_SZ];
|
||||
};
|
||||
|
||||
/* V4L2 controls supported by the driver */
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
|
||||
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
|
||||
|
||||
static const struct ctrl sd_ctrls[] = {
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = -128,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
#define BRIGHTNESS_DEF 0
|
||||
.default_value = BRIGHTNESS_DEF,
|
||||
},
|
||||
.set = sd_setbrightness,
|
||||
.get = sd_getbrightness,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_CONTRAST,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Contrast",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
#define CONTRAST_DEF 0x20
|
||||
.default_value = CONTRAST_DEF,
|
||||
},
|
||||
.set = sd_setcontrast,
|
||||
.get = sd_getcontrast,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Color",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
#define COLOR_DEF 0x1a
|
||||
.default_value = COLOR_DEF,
|
||||
},
|
||||
.set = sd_setcolors,
|
||||
.get = sd_getcolors,
|
||||
},
|
||||
{
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
#define AUTOGAIN_DEF 1
|
||||
.default_value = AUTOGAIN_DEF,
|
||||
},
|
||||
.set = sd_setautogain,
|
||||
.get = sd_getautogain,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct v4l2_pix_format vga_mode[] = {
|
||||
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
|
||||
.bytesperline = 320,
|
||||
|
@ -597,31 +523,31 @@ static void spca504B_setQtable(struct gspca_dev *gspca_dev)
|
|||
spca504B_PollingDataReady(gspca_dev);
|
||||
}
|
||||
|
||||
static void setbrightness(struct gspca_dev *gspca_dev)
|
||||
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u16 reg;
|
||||
|
||||
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
|
||||
reg_w_riv(gspca_dev, 0x00, reg, sd->brightness);
|
||||
reg_w_riv(gspca_dev, 0x00, reg, val);
|
||||
}
|
||||
|
||||
static void setcontrast(struct gspca_dev *gspca_dev)
|
||||
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u16 reg;
|
||||
|
||||
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
|
||||
reg_w_riv(gspca_dev, 0x00, reg, sd->contrast);
|
||||
reg_w_riv(gspca_dev, 0x00, reg, val);
|
||||
}
|
||||
|
||||
static void setcolors(struct gspca_dev *gspca_dev)
|
||||
static void setcolors(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u16 reg;
|
||||
|
||||
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
|
||||
reg_w_riv(gspca_dev, 0x00, reg, sd->colors);
|
||||
reg_w_riv(gspca_dev, 0x00, reg, val);
|
||||
}
|
||||
|
||||
static void init_ctl_reg(struct gspca_dev *gspca_dev)
|
||||
|
@ -629,10 +555,6 @@ static void init_ctl_reg(struct gspca_dev *gspca_dev)
|
|||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int pollreg = 1;
|
||||
|
||||
setbrightness(gspca_dev);
|
||||
setcontrast(gspca_dev);
|
||||
setcolors(gspca_dev);
|
||||
|
||||
switch (sd->bridge) {
|
||||
case BRIDGE_SPCA504:
|
||||
case BRIDGE_SPCA504C:
|
||||
|
@ -704,11 +626,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
cam->nmodes = ARRAY_SIZE(vga_mode2);
|
||||
break;
|
||||
}
|
||||
sd->brightness = BRIGHTNESS_DEF;
|
||||
sd->contrast = CONTRAST_DEF;
|
||||
sd->colors = COLOR_DEF;
|
||||
sd->autogain = AUTOGAIN_DEF;
|
||||
sd->quality = QUALITY_DEF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -807,7 +724,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
/* create the JPEG header */
|
||||
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
|
||||
0x22); /* JPEG 411 */
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
jpeg_set_qual(sd->jpeg_hdr, QUALITY);
|
||||
|
||||
if (sd->bridge == BRIDGE_SPCA504B)
|
||||
spca504B_setQtable(gspca_dev);
|
||||
|
@ -1012,116 +929,69 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
|
||||
}
|
||||
|
||||
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
sd->brightness = val;
|
||||
if (gspca_dev->streaming)
|
||||
setbrightness(gspca_dev);
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
setbrightness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
setcontrast(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
setcolors(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
sd->autogain = ctrl->val;
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
*val = sd->brightness;
|
||||
return 0;
|
||||
}
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 4);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_CONTRAST, 0, 255, 1, 0x20);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 255, 1, 0x1a);
|
||||
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
|
||||
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->contrast = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcontrast(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->contrast;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->colors = val;
|
||||
if (gspca_dev->streaming)
|
||||
setcolors(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->colors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
sd->autogain = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
*val = sd->autogain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (jcomp->quality < QUALITY_MIN)
|
||||
sd->quality = QUALITY_MIN;
|
||||
else if (jcomp->quality > QUALITY_MAX)
|
||||
sd->quality = QUALITY_MAX;
|
||||
else
|
||||
sd->quality = jcomp->quality;
|
||||
if (gspca_dev->streaming)
|
||||
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
||||
struct v4l2_jpegcompression *jcomp)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
memset(jcomp, 0, sizeof *jcomp);
|
||||
jcomp->quality = sd->quality;
|
||||
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
||||
| V4L2_JPEG_MARKER_DQT;
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sub-driver description */
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = MODULE_NAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = ARRAY_SIZE(sd_ctrls),
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
.pkt_scan = sd_pkt_scan,
|
||||
.get_jcomp = sd_get_jcomp,
|
||||
.set_jcomp = sd_set_jcomp,
|
||||
};
|
||||
|
||||
/* -- module initialisation -- */
|
||||
|
@ -1208,6 +1078,7 @@ static struct usb_driver sd_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = gspca_suspend,
|
||||
.resume = gspca_resume,
|
||||
.reset_resume = gspca_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -120,24 +120,13 @@ static const u8 jpeg_head[] = {
|
|||
#define JPEG_HDR_SZ 521
|
||||
};
|
||||
|
||||
enum e_ctrl {
|
||||
EXPOSURE,
|
||||
QUALITY,
|
||||
SHARPNESS,
|
||||
RGAIN,
|
||||
GAIN,
|
||||
BGAIN,
|
||||
GAMMA,
|
||||
AUTOGAIN,
|
||||
NCTRLS /* number of controls */
|
||||
};
|
||||
|
||||
#define AUTOGAIN_DEF 1
|
||||
|
||||
struct sd {
|
||||
struct gspca_dev gspca_dev; /* !! must be the first item */
|
||||
|
||||
struct gspca_ctrl ctrls[NCTRLS];
|
||||
struct v4l2_ctrl *jpegqual;
|
||||
struct v4l2_ctrl *sharpness;
|
||||
struct v4l2_ctrl *gamma;
|
||||
struct v4l2_ctrl *blue;
|
||||
struct v4l2_ctrl *red;
|
||||
|
||||
u8 framerate;
|
||||
u8 quality; /* webcam current JPEG quality (0..16) */
|
||||
|
@ -1415,32 +1404,33 @@ static void soi763a_6810_init(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
|
||||
/* set the gain and exposure */
|
||||
static void setexposure(struct gspca_dev *gspca_dev)
|
||||
static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain,
|
||||
s32 blue, s32 red)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->sensor == SENSOR_CX0342) {
|
||||
int expo;
|
||||
|
||||
expo = (sd->ctrls[EXPOSURE].val << 2) - 1;
|
||||
expo = (expo << 2) - 1;
|
||||
i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
|
||||
i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
|
||||
if (sd->bridge == BRIDGE_TP6800)
|
||||
i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
|
||||
sd->ctrls[GAIN].val >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val);
|
||||
gain >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain);
|
||||
if (sd->bridge == BRIDGE_TP6800)
|
||||
i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
|
||||
sd->ctrls[GAIN].val >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, sd->ctrls[GAIN].val);
|
||||
if (sd->bridge == BRIDGE_TP6800)
|
||||
i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
|
||||
sd->ctrls[BGAIN].val >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, sd->ctrls[BGAIN].val);
|
||||
if (sd->bridge == BRIDGE_TP6800)
|
||||
i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
|
||||
sd->ctrls[RGAIN].val >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, sd->ctrls[RGAIN].val);
|
||||
gain >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain);
|
||||
if (sd->sensor == SENSOR_CX0342) {
|
||||
if (sd->bridge == BRIDGE_TP6800)
|
||||
i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
|
||||
blue >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue);
|
||||
if (sd->bridge == BRIDGE_TP6800)
|
||||
i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
|
||||
red >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red);
|
||||
}
|
||||
i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
|
||||
sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
|
||||
return;
|
||||
|
@ -1448,10 +1438,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
|||
|
||||
/* soi763a */
|
||||
i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */
|
||||
sd->ctrls[EXPOSURE].val);
|
||||
expo);
|
||||
/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */
|
||||
i2c_w(gspca_dev, 0x00, /* gain */
|
||||
sd->ctrls[GAIN].val);
|
||||
gain);
|
||||
}
|
||||
|
||||
/* set the JPEG quantization tables */
|
||||
|
@ -1472,12 +1462,10 @@ static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
|
|||
}
|
||||
|
||||
/* set the JPEG compression quality factor */
|
||||
static void setquality(struct gspca_dev *gspca_dev)
|
||||
static void setquality(struct gspca_dev *gspca_dev, s32 q)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u16 q;
|
||||
|
||||
q = sd->ctrls[QUALITY].val;
|
||||
if (q != 16)
|
||||
q = 15 - q;
|
||||
|
||||
|
@ -1508,10 +1496,9 @@ static const u8 color_gain[NSENSORS][18] = {
|
|||
0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
|
||||
};
|
||||
|
||||
static void setgamma(struct gspca_dev *gspca_dev)
|
||||
static void setgamma(struct gspca_dev *gspca_dev, s32 gamma)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int gamma;
|
||||
#define NGAMMA 6
|
||||
static const u8 gamma_tb[NGAMMA][3][1024] = {
|
||||
{ /* gamma 0 - from tp6800 + soi763a */
|
||||
|
@ -3836,7 +3823,6 @@ static void setgamma(struct gspca_dev *gspca_dev)
|
|||
if (sd->bridge == BRIDGE_TP6810)
|
||||
reg_w(gspca_dev, 0x02, 0x28);
|
||||
/* msleep(50); */
|
||||
gamma = sd->ctrls[GAMMA].val;
|
||||
bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
|
||||
bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
|
||||
bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
|
||||
|
@ -3864,43 +3850,35 @@ static void setgamma(struct gspca_dev *gspca_dev)
|
|||
/* msleep(50); */
|
||||
}
|
||||
|
||||
static void setsharpness(struct gspca_dev *gspca_dev)
|
||||
static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 val;
|
||||
|
||||
if (sd->bridge == BRIDGE_TP6800) {
|
||||
val = sd->ctrls[SHARPNESS].val
|
||||
| 0x08; /* grid compensation enable */
|
||||
val |= 0x08; /* grid compensation enable */
|
||||
if (gspca_dev->width == 640)
|
||||
reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
|
||||
else
|
||||
val |= 0x04; /* scaling down enable */
|
||||
reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
|
||||
} else {
|
||||
val = (sd->ctrls[SHARPNESS].val << 5) | 0x08;
|
||||
val = (val << 5) | 0x08;
|
||||
reg_w(gspca_dev, 0x59, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void setautogain(struct gspca_dev *gspca_dev)
|
||||
static void setautogain(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
|
||||
return;
|
||||
if (sd->ctrls[AUTOGAIN].val) {
|
||||
sd->ag_cnt = AG_CNT_START;
|
||||
gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
|
||||
} else {
|
||||
sd->ag_cnt = -1;
|
||||
gspca_dev->ctrl_inac &= ~((1 << EXPOSURE) | (1 << GAIN));
|
||||
}
|
||||
sd->ag_cnt = val ? AG_CNT_START : -1;
|
||||
}
|
||||
|
||||
/* set the resolution for sensor cx0342 */
|
||||
static void set_resolution(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
|
||||
if (gspca_dev->width == 320) {
|
||||
reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
|
||||
|
@ -3926,8 +3904,9 @@ static void set_resolution(struct gspca_dev *gspca_dev)
|
|||
i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
|
||||
bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
|
||||
ARRAY_SIZE(color_gain[0]));
|
||||
setgamma(gspca_dev);
|
||||
setquality(gspca_dev);
|
||||
setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
|
||||
if (sd->sensor == SENSOR_SOI763A)
|
||||
setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
|
||||
}
|
||||
|
||||
/* convert the frame rate to a tp68x0 value */
|
||||
|
@ -3963,7 +3942,7 @@ static int get_fr_idx(struct gspca_dev *gspca_dev)
|
|||
return i;
|
||||
}
|
||||
|
||||
static void setframerate(struct gspca_dev *gspca_dev)
|
||||
static void setframerate(struct gspca_dev *gspca_dev, s32 val)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
u8 fr_idx;
|
||||
|
@ -3974,7 +3953,7 @@ static void setframerate(struct gspca_dev *gspca_dev)
|
|||
reg_r(gspca_dev, 0x7b);
|
||||
reg_w(gspca_dev, 0x7b,
|
||||
sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
|
||||
if (sd->ctrls[EXPOSURE].val >= 128)
|
||||
if (val >= 128)
|
||||
fr_idx = 0xf0; /* lower frame rate */
|
||||
}
|
||||
|
||||
|
@ -3984,43 +3963,43 @@ static void setframerate(struct gspca_dev *gspca_dev)
|
|||
i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
|
||||
}
|
||||
|
||||
static void setrgain(struct gspca_dev *gspca_dev)
|
||||
static void setrgain(struct gspca_dev *gspca_dev, s32 rgain)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int rgain;
|
||||
|
||||
rgain = sd->ctrls[RGAIN].val;
|
||||
i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
|
||||
i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
|
||||
}
|
||||
|
||||
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
|
||||
static int sd_setgain(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
s32 val = gspca_dev->gain->val;
|
||||
|
||||
if (sd->sensor == SENSOR_CX0342) {
|
||||
sd->ctrls[BGAIN].val = sd->ctrls[BGAIN].val
|
||||
* val / sd->ctrls[GAIN].val;
|
||||
if (sd->ctrls[BGAIN].val > 4095)
|
||||
sd->ctrls[BGAIN].val = 4095;
|
||||
sd->ctrls[RGAIN].val = sd->ctrls[RGAIN].val
|
||||
* val / sd->ctrls[GAIN].val;
|
||||
if (sd->ctrls[RGAIN].val > 4095)
|
||||
sd->ctrls[RGAIN].val = 4095;
|
||||
s32 old = gspca_dev->gain->cur.val ?
|
||||
gspca_dev->gain->cur.val : 1;
|
||||
|
||||
sd->blue->val = sd->blue->val * val / old;
|
||||
if (sd->blue->val > 4095)
|
||||
sd->blue->val = 4095;
|
||||
sd->red->val = sd->red->val * val / old;
|
||||
if (sd->red->val > 4095)
|
||||
sd->red->val = 4095;
|
||||
}
|
||||
if (gspca_dev->streaming) {
|
||||
if (sd->sensor == SENSOR_CX0342)
|
||||
setexposure(gspca_dev, gspca_dev->exposure->val,
|
||||
gspca_dev->gain->val,
|
||||
sd->blue->val, sd->red->val);
|
||||
else
|
||||
setexposure(gspca_dev, gspca_dev->exposure->val,
|
||||
gspca_dev->gain->val, 0, 0);
|
||||
}
|
||||
sd->ctrls[GAIN].val = val;
|
||||
if (gspca_dev->streaming)
|
||||
setexposure(gspca_dev);
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static void setbgain(struct gspca_dev *gspca_dev)
|
||||
static void setbgain(struct gspca_dev *gspca_dev, s32 bgain)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
int bgain;
|
||||
|
||||
bgain = sd->ctrls[BGAIN].val;
|
||||
i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
|
||||
i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
|
||||
i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
|
||||
|
@ -4040,7 +4019,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|||
framerates : framerates_6810;
|
||||
|
||||
sd->framerate = 30; /* default: 30 fps */
|
||||
gspca_dev->cam.ctrls = sd->ctrls;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4108,32 +4086,16 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|||
}
|
||||
if (sd->sensor == SENSOR_SOI763A) {
|
||||
pr_info("Sensor soi763a\n");
|
||||
sd->ctrls[GAMMA].def = sd->bridge == BRIDGE_TP6800 ? 0 : 1;
|
||||
sd->ctrls[GAIN].max = 15;
|
||||
sd->ctrls[GAIN].def = 3;
|
||||
gspca_dev->ctrl_dis = (1 << RGAIN) | (1 << BGAIN);
|
||||
if (sd->bridge == BRIDGE_TP6810) {
|
||||
soi763a_6810_init(gspca_dev);
|
||||
#if AUTOGAIN_DEF
|
||||
gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
|
||||
#endif
|
||||
} else {
|
||||
gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
|
||||
}
|
||||
} else {
|
||||
pr_info("Sensor cx0342\n");
|
||||
if (sd->bridge == BRIDGE_TP6810) {
|
||||
cx0342_6810_init(gspca_dev);
|
||||
#if AUTOGAIN_DEF
|
||||
gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
|
||||
#endif
|
||||
} else {
|
||||
gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
|
||||
}
|
||||
}
|
||||
|
||||
if (sd->bridge == BRIDGE_TP6810)
|
||||
sd->ctrls[QUALITY].def = 0; /* auto quality */
|
||||
set_dqt(gspca_dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
@ -4207,8 +4169,9 @@ static void set_led(struct gspca_dev *gspca_dev, int on)
|
|||
|
||||
static void cx0342_6800_start(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
static const struct cmd reg_init[] = {
|
||||
/*fixme: is this usefull?*/
|
||||
/* fixme: is this useful? */
|
||||
{TP6800_R17_GPIO_IO, 0x9f},
|
||||
{TP6800_R16_GPIO_PD, 0x40},
|
||||
{TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
|
||||
|
@ -4279,13 +4242,21 @@ static void cx0342_6800_start(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
|
||||
i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
|
||||
i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
|
||||
setexposure(gspca_dev);
|
||||
if (sd->sensor == SENSOR_CX0342)
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain),
|
||||
v4l2_ctrl_g_ctrl(sd->blue),
|
||||
v4l2_ctrl_g_ctrl(sd->red));
|
||||
else
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
|
||||
set_led(gspca_dev, 1);
|
||||
set_resolution(gspca_dev);
|
||||
}
|
||||
|
||||
static void cx0342_6810_start(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
static const struct cmd sensor_init_2[] = {
|
||||
{CX0342_EXPO_LINE_L, 0x6f},
|
||||
{CX0342_EXPO_LINE_H, 0x02},
|
||||
|
@ -4366,10 +4337,10 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x07, 0x85);
|
||||
reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
|
||||
}
|
||||
setgamma(gspca_dev);
|
||||
setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
|
||||
reg_w_buf(gspca_dev, tp6810_bridge_start,
|
||||
ARRAY_SIZE(tp6810_bridge_start));
|
||||
setsharpness(gspca_dev);
|
||||
setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
|
||||
bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
|
||||
ARRAY_SIZE(color_gain[0]));
|
||||
reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
|
||||
|
@ -4380,11 +4351,12 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
|
|||
i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
|
||||
|
||||
set_led(gspca_dev, 1);
|
||||
/* setquality(gspca_dev); */
|
||||
/* setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */
|
||||
}
|
||||
|
||||
static void soi763a_6800_start(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
static const struct cmd reg_init[] = {
|
||||
{TP6800_R79_QUALITY, 0x04},
|
||||
{TP6800_R79_QUALITY, 0x01},
|
||||
|
@ -4484,19 +4456,28 @@ static void soi763a_6800_start(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
|
||||
reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
|
||||
|
||||
setsharpness(gspca_dev);
|
||||
setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
|
||||
|
||||
bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
|
||||
ARRAY_SIZE(color_gain[0]));
|
||||
|
||||
set_led(gspca_dev, 1);
|
||||
setexposure(gspca_dev);
|
||||
setquality(gspca_dev);
|
||||
setgamma(gspca_dev);
|
||||
if (sd->sensor == SENSOR_CX0342)
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain),
|
||||
v4l2_ctrl_g_ctrl(sd->blue),
|
||||
v4l2_ctrl_g_ctrl(sd->red));
|
||||
else
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
|
||||
if (sd->sensor == SENSOR_SOI763A)
|
||||
setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
|
||||
setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
|
||||
}
|
||||
|
||||
static void soi763a_6810_start(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
static const struct cmd bridge_init_2[] = {
|
||||
{TP6800_R7A_BLK_THRLD, 0x00},
|
||||
{TP6800_R79_QUALITY, 0x04},
|
||||
|
@ -4520,7 +4501,14 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x22, gspca_dev->alt);
|
||||
bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
|
||||
reg_w(gspca_dev, 0x59, 0x40);
|
||||
setexposure(gspca_dev);
|
||||
if (sd->sensor == SENSOR_CX0342)
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain),
|
||||
v4l2_ctrl_g_ctrl(sd->blue),
|
||||
v4l2_ctrl_g_ctrl(sd->red));
|
||||
else
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
|
||||
reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
|
||||
reg_w_buf(gspca_dev, tp6810_ov_init_common,
|
||||
ARRAY_SIZE(tp6810_ov_init_common));
|
||||
|
@ -4534,7 +4522,7 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x07, 0x85);
|
||||
reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
|
||||
}
|
||||
setgamma(gspca_dev);
|
||||
setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
|
||||
reg_w_buf(gspca_dev, tp6810_bridge_start,
|
||||
ARRAY_SIZE(tp6810_bridge_start));
|
||||
|
||||
|
@ -4545,12 +4533,19 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
|
|||
|
||||
reg_w(gspca_dev, 0x00, 0x00);
|
||||
|
||||
setsharpness(gspca_dev);
|
||||
setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
|
||||
bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
|
||||
ARRAY_SIZE(color_gain[0]));
|
||||
set_led(gspca_dev, 1);
|
||||
reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
|
||||
setexposure(gspca_dev);
|
||||
if (sd->sensor == SENSOR_CX0342)
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain),
|
||||
v4l2_ctrl_g_ctrl(sd->blue),
|
||||
v4l2_ctrl_g_ctrl(sd->red));
|
||||
else
|
||||
setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
|
||||
reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
|
||||
}
|
||||
|
||||
|
@ -4576,12 +4571,25 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|||
reg_w(gspca_dev, 0x80, 0x03);
|
||||
reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
|
||||
|
||||
setexposure(gspca_dev);
|
||||
setquality(gspca_dev);
|
||||
setautogain(gspca_dev);
|
||||
if (sd->sensor == SENSOR_CX0342)
|
||||
setexposure(gspca_dev,
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain),
|
||||
v4l2_ctrl_g_ctrl(sd->blue),
|
||||
v4l2_ctrl_g_ctrl(sd->red));
|
||||
else
|
||||
setexposure(gspca_dev,
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->exposure),
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
|
||||
if (sd->sensor == SENSOR_SOI763A)
|
||||
setquality(gspca_dev,
|
||||
v4l2_ctrl_g_ctrl(sd->jpegqual));
|
||||
if (sd->bridge == BRIDGE_TP6810)
|
||||
setautogain(gspca_dev,
|
||||
v4l2_ctrl_g_ctrl(gspca_dev->autogain));
|
||||
}
|
||||
|
||||
setframerate(gspca_dev);
|
||||
setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
|
||||
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
@ -4672,12 +4680,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|||
}
|
||||
}
|
||||
|
||||
/* -- do autogain -- */
|
||||
/* gain setting is done in setexposure() for tp6810 */
|
||||
static void setgain(struct gspca_dev *gspca_dev) {}
|
||||
#define WANT_REGULAR_AUTOGAIN
|
||||
#include "autogain_functions.h"
|
||||
|
||||
static void sd_dq_callback(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
@ -4739,17 +4741,19 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
|
|||
luma /= 4;
|
||||
reg_w(gspca_dev, 0x7d, 0x00);
|
||||
|
||||
expo = sd->ctrls[EXPOSURE].val;
|
||||
ret = auto_gain_n_exposure(gspca_dev, luma,
|
||||
expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
|
||||
ret = gspca_expo_autogain(gspca_dev, luma,
|
||||
60, /* desired luma */
|
||||
6, /* dead zone */
|
||||
2, /* gain knee */
|
||||
70); /* expo knee */
|
||||
sd->ag_cnt = AG_CNT_START;
|
||||
if (sd->bridge == BRIDGE_TP6810) {
|
||||
if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128)
|
||||
|| (expo < 128 && sd->ctrls[EXPOSURE].val >= 128))
|
||||
setframerate(gspca_dev);
|
||||
int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
|
||||
|
||||
if ((expo >= 128 && new_expo < 128)
|
||||
|| (expo < 128 && new_expo >= 128))
|
||||
setframerate(gspca_dev, new_expo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -4789,7 +4793,7 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
|
|||
|
||||
sd->framerate = tpf->denominator / tpf->numerator;
|
||||
if (gspca_dev->streaming)
|
||||
setframerate(gspca_dev);
|
||||
setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
|
||||
|
||||
/* Return the actual framerate */
|
||||
i = get_fr_idx(gspca_dev);
|
||||
|
@ -4806,12 +4810,10 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
|
|||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->sensor == SENSOR_SOI763A)
|
||||
jpeg_set_qual(sd->jpeg_hdr, jcomp->quality);
|
||||
/* else
|
||||
fixme: TODO
|
||||
*/
|
||||
return gspca_dev->usb_err;
|
||||
if (sd->sensor != SENSOR_SOI763A)
|
||||
return -ENOTTY;
|
||||
v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
||||
|
@ -4819,118 +4821,109 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
|
|||
{
|
||||
struct sd *sd = (struct sd *) gspca_dev;
|
||||
|
||||
if (sd->sensor != SENSOR_SOI763A)
|
||||
return -ENOTTY;
|
||||
memset(jcomp, 0, sizeof *jcomp);
|
||||
jcomp->quality = jpeg_q[sd->quality];
|
||||
jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
|
||||
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
|
||||
| V4L2_JPEG_MARKER_DQT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ctrl sd_ctrls[NCTRLS] = {
|
||||
[EXPOSURE] = {
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0x01,
|
||||
.maximum = 0xdc,
|
||||
.step = 1,
|
||||
.default_value = 0x4e,
|
||||
},
|
||||
.set_control = setexposure
|
||||
},
|
||||
[QUALITY] = {
|
||||
{
|
||||
.id = V4L2_CID_PRIVATE_BASE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Compression quality",
|
||||
.minimum = 0,
|
||||
.maximum = 15,
|
||||
.step = 1,
|
||||
.default_value = 13,
|
||||
},
|
||||
.set_control = setquality
|
||||
},
|
||||
[RGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_RED_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Red balance",
|
||||
.minimum = 0,
|
||||
.maximum = 4095,
|
||||
.step = 1,
|
||||
.default_value = 256,
|
||||
},
|
||||
.set_control = setrgain
|
||||
},
|
||||
[GAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 4095,
|
||||
.step = 1,
|
||||
.default_value = 256,
|
||||
},
|
||||
.set = sd_setgain
|
||||
},
|
||||
[BGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_BLUE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Blue balance",
|
||||
.minimum = 0,
|
||||
.maximum = 4095,
|
||||
.step = 1,
|
||||
.default_value = 256,
|
||||
},
|
||||
.set_control = setbgain
|
||||
},
|
||||
[SHARPNESS] = {
|
||||
{
|
||||
.id = V4L2_CID_SHARPNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Sharpness",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
.default_value = 2,
|
||||
},
|
||||
.set_control = setsharpness
|
||||
},
|
||||
[GAMMA] = {
|
||||
{
|
||||
.id = V4L2_CID_GAMMA,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gamma",
|
||||
.minimum = 0,
|
||||
.maximum = NGAMMA - 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
.set_control = setgamma
|
||||
},
|
||||
[AUTOGAIN] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = AUTOGAIN_DEF
|
||||
},
|
||||
.set_control = setautogain
|
||||
},
|
||||
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct gspca_dev *gspca_dev =
|
||||
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
|
||||
gspca_dev->usb_err = 0;
|
||||
|
||||
if (!gspca_dev->streaming)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_SHARPNESS:
|
||||
setsharpness(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_GAMMA:
|
||||
setgamma(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
setbgain(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
setrgain(gspca_dev, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
sd_setgain(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
if (ctrl->val)
|
||||
break;
|
||||
sd_setgain(gspca_dev);
|
||||
break;
|
||||
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
|
||||
jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
|
||||
break;
|
||||
}
|
||||
return gspca_dev->usb_err;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
|
||||
.s_ctrl = sd_s_ctrl,
|
||||
};
|
||||
|
||||
static int sd_init_controls(struct gspca_dev *gspca_dev)
|
||||
{
|
||||
struct sd *sd = (struct sd *)gspca_dev;
|
||||
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
|
||||
|
||||
gspca_dev->vdev.ctrl_handler = hdl;
|
||||
v4l2_ctrl_handler_init(hdl, 4);
|
||||
gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e);
|
||||
if (sd->sensor == SENSOR_CX0342) {
|
||||
sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
|
||||
sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
|
||||
}
|
||||
if (sd->sensor == SENSOR_SOI763A)
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 15, 1, 3);
|
||||
else
|
||||
gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 4095, 1, 256);
|
||||
sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_SHARPNESS, 0, 3, 1, 2);
|
||||
sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_GAMMA, 0, NGAMMA - 1, 1,
|
||||
(sd->sensor == SENSOR_SOI763A &&
|
||||
sd->bridge == BRIDGE_TP6800) ? 0 : 1);
|
||||
if (sd->bridge == BRIDGE_TP6810)
|
||||
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
if (sd->sensor == SENSOR_SOI763A)
|
||||
sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
||||
V4L2_CID_JPEG_COMPRESSION_QUALITY,
|
||||
0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13);
|
||||
|
||||
if (hdl->error) {
|
||||
pr_err("Could not initialize controls\n");
|
||||
return hdl->error;
|
||||
}
|
||||
if (gspca_dev->autogain)
|
||||
v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
|
||||
else
|
||||
v4l2_ctrl_cluster(2, &gspca_dev->exposure);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sd_desc sd_desc = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.ctrls = sd_ctrls,
|
||||
.nctrls = NCTRLS,
|
||||
.config = sd_config,
|
||||
.init = sd_init,
|
||||
.init_controls = sd_init_controls,
|
||||
.isoc_init = sd_isoc_init,
|
||||
.start = sd_start,
|
||||
.stopN = sd_stopN,
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче