1
0
Fork 0

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
wifi-calibration
Linus Torvalds 2012-07-31 18:47:44 -07:00
commit 8762541f06
146 changed files with 7523 additions and 8151 deletions

View File

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

View File

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

View File

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

View File

@ -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&nbsp;v4l2_format</entry>
<entry>&v4l2-format;</entry>
<entry><structfield>format</structfield></entry>
<entry>Filled in by the application, preserved by the driver.</entry>
</row>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = &reg->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,

View File

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

View File

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

View File

@ -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(&current->mm->mmap_sem);
res = get_user_pages(current, current->mm,
virtp, nr_pages, 1, 0, &pages, NULL);
up_read(&current->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,

View File

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

View File

@ -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(&current->mm->mmap_sem);
res = get_user_pages(current, current->mm,
virtp, nr_pages, 1, 0, &pages, NULL);
up_read(&current->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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -299,6 +299,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
.reset_resume = gspca_resume,
#endif
};

View File

@ -521,6 +521,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
.reset_resume = gspca_resume,
#endif
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -462,6 +462,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
.reset_resume = gspca_resume,
#endif
};

View File

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

View File

@ -694,6 +694,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
.reset_resume = gspca_resume,
#endif
};

View File

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

View File

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

View File

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

View File

@ -3199,6 +3199,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
.reset_resume = gspca_resume,
#endif
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -433,6 +433,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
.reset_resume = gspca_resume,
#endif
};

View File

@ -340,6 +340,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
.reset_resume = gspca_resume,
#endif
};

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

Some files were not shown because too many files have changed in this diff Show More