diff --git a/[refs] b/[refs] index 168be923c587..fe7825658e18 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 1871e845e564c4e17f561ec4e5e4bb6bb8578685 +refs/heads/master: e29b72f5e129b4dd4b77dc01dba340006bb103f8 diff --git a/trunk/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads b/trunk/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads deleted file mode 100644 index b0b0eeb20fe3..000000000000 --- a/trunk/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads +++ /dev/null @@ -1,5 +0,0 @@ -What: /proc/sys/vm/nr_pdflush_threads -Date: June 2012 -Contact: Wanpeng Li -Description: Since pdflush is replaced by per-BDI flusher, the interface of old pdflush - exported in /proc/sys/vm/ should be removed. diff --git a/trunk/Documentation/ABI/testing/sysfs-bus-rbd b/trunk/Documentation/ABI/testing/sysfs-bus-rbd index 3c17b62899f6..bcd88eb7ebcd 100644 --- a/trunk/Documentation/ABI/testing/sysfs-bus-rbd +++ b/trunk/Documentation/ABI/testing/sysfs-bus-rbd @@ -35,14 +35,8 @@ name pool - The name of the storage pool where this rbd image resides. - An rbd image name is unique within its pool. - -pool_id - - The unique identifier for the rbd image's pool. This is - a permanent attribute of the pool. A pool's id will never - change. + The pool where this rbd image resides. The pool-name pair is unique + per rados system. size diff --git a/trunk/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb b/trunk/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb deleted file mode 100644 index 2107082426da..000000000000 --- a/trunk/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb +++ /dev/null @@ -1,44 +0,0 @@ -What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_alpha -Date: May 2012 -Contact: Laurent Pinchart -Description: - This file is only available on fb[0-9] devices corresponding - to overlay planes. - - Stores the alpha blending value for the overlay. Values range - from 0 (transparent) to 255 (opaque). The value is ignored if - the mode is not set to Alpha Blending. - -What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_mode -Date: May 2012 -Contact: Laurent Pinchart -Description: - This file is only available on fb[0-9] devices corresponding - to overlay planes. - - Selects the composition mode for the overlay. Possible values - are - - 0 - Alpha Blending - 1 - ROP3 - -What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_position -Date: May 2012 -Contact: Laurent Pinchart -Description: - This file is only available on fb[0-9] devices corresponding - to overlay planes. - - Stores the x,y overlay position on the display in pixels. The - position format is `[0-9]+,[0-9]+'. - -What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_rop3 -Date: May 2012 -Contact: Laurent Pinchart -Description: - This file is only available on fb[0-9] devices corresponding - to overlay planes. - - Stores the raster operation (ROP3) for the overlay. Values - range from 0 to 255. The value is ignored if the mode is not - set to ROP3. diff --git a/trunk/Documentation/DocBook/media/v4l/compat.xml b/trunk/Documentation/DocBook/media/v4l/compat.xml index faa0fd14666a..97b895151bb0 100644 --- a/trunk/Documentation/DocBook/media/v4l/compat.xml +++ b/trunk/Documentation/DocBook/media/v4l/compat.xml @@ -2460,7 +2460,7 @@ that used it. It was originally scheduled for removal in 2.6.35.
- V4L2 in Linux 3.6 + V4L2 in Linux 3.5 Replaced input in @@ -2471,24 +2471,6 @@ that used it. It was originally scheduled for removal in 2.6.35.
-
- V4L2 in Linux 3.6 - - - Added V4L2_CAP_VIDEO_M2M and V4L2_CAP_VIDEO_M2M_MPLANE capabilities. - - -
- -
- V4L2 in Linux 3.6 - - - Added support for frequency band enumerations: &VIDIOC-ENUM-FREQ-BANDS;. - - -
-
Relation of V4L2 to other Linux multimedia APIs @@ -2618,9 +2600,6 @@ ioctls. V4L2_CID_AUTO_FOCUS_AREA control. - - Support for frequency band enumeration: &VIDIOC-ENUM-FREQ-BANDS; ioctl. -
diff --git a/trunk/Documentation/DocBook/media/v4l/controls.xml b/trunk/Documentation/DocBook/media/v4l/controls.xml index b0964fb4e834..cda0dfb6769a 100644 --- a/trunk/Documentation/DocBook/media/v4l/controls.xml +++ b/trunk/Documentation/DocBook/media/v4l/controls.xml @@ -372,11 +372,6 @@ minimum value disables backlight compensation. Cr component, bits [15:8] as Cb component and bits [31:16] must be zero. - - V4L2_CID_AUTOBRIGHTNESS - boolean - Enable Automatic Brightness. - V4L2_CID_ROTATE integer diff --git a/trunk/Documentation/DocBook/media/v4l/v4l2.xml b/trunk/Documentation/DocBook/media/v4l/v4l2.xml index eee6908c749f..36bafc48e03b 100644 --- a/trunk/Documentation/DocBook/media/v4l/v4l2.xml +++ b/trunk/Documentation/DocBook/media/v4l/v4l2.xml @@ -140,11 +140,6 @@ structs, ioctls) must be noted in more detail in the history chapter applications. --> - 3.6 - 2012-07-02 - hv - Added VIDIOC_ENUM_FREQ_BANDS. - 3.5 2012-05-07 sa, sn @@ -539,7 +534,6 @@ and discussions on the V4L mailing list. &sub-enum-fmt; &sub-enum-framesizes; &sub-enum-frameintervals; - &sub-enum-freq-bands; &sub-enuminput; &sub-enumoutput; &sub-enumstd; diff --git a/trunk/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/trunk/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index a8cda1acacd9..5e73b1c8d095 100644 --- a/trunk/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/trunk/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -64,7 +64,7 @@ different sizes. To allocate device buffers applications initialize relevant fields of the v4l2_create_buffers structure. They set the type field in the -&v4l2-format; structure, embedded in this +v4l2_format structure, embedded in this structure, to the respective stream or buffer type. count must be set to the number of required buffers. memory specifies the required I/O method. The @@ -114,7 +114,7 @@ information. /> - &v4l2-format; + struct v4l2_format format Filled in by the application, preserved by the driver. diff --git a/trunk/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml b/trunk/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml index cd7720d404ea..6673ce582050 100644 --- a/trunk/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml +++ b/trunk/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml @@ -54,9 +54,15 @@ interface and may change in the future. - 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. + To query the available timings, applications initialize the +index field and zero the reserved array of &v4l2-dv-timings-cap; +and call the VIDIOC_DV_TIMINGS_CAP 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 EINVAL. Note that drivers may enumerate a +different set of DV timings after switching the video input or +output. struct <structname>v4l2_bt_timings_cap</structname> @@ -109,7 +115,7 @@ different values after switching the video input or output. __u32 reserved[16] - Reserved for future extensions. Drivers must set the array to zero. + diff --git a/trunk/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml b/trunk/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml deleted file mode 100644 index 6541ba0175ed..000000000000 --- a/trunk/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - ioctl VIDIOC_ENUM_FREQ_BANDS - &manvol; - - - - VIDIOC_ENUM_FREQ_BANDS - Enumerate supported frequency bands - - - - - - int ioctl - int fd - int request - struct v4l2_frequency_band -*argp - - - - - - Arguments - - - - fd - - &fd; - - - - request - - VIDIOC_ENUM_FREQ_BANDS - - - - argp - - - - - - - - - Description - - - Experimental - This is an experimental - interface and may change in the future. - - - Enumerates the frequency bands that a tuner or modulator supports. -To do this applications initialize the tuner, -type and index fields, -and zero out the reserved array of a &v4l2-frequency-band; and -call the VIDIOC_ENUM_FREQ_BANDS ioctl with a pointer -to this structure. - - This ioctl is supported if the V4L2_TUNER_CAP_FREQ_BANDS capability - of the corresponding tuner/modulator is set. - -
- struct <structname>v4l2_frequency_band</structname> - - &cs-str; - - - __u32 - tuner - The tuner or modulator index number. This is the -same value as in the &v4l2-input; tuner -field and the &v4l2-tuner; index field, or -the &v4l2-output; modulator field and the -&v4l2-modulator; index field. - - - __u32 - type - The tuner type. This is the same value as in the -&v4l2-tuner; type field. The type must be set -to V4L2_TUNER_RADIO for /dev/radioX -device nodes, and to V4L2_TUNER_ANALOG_TV -for all others. Set this field to V4L2_TUNER_RADIO for -modulators (currently only radio modulators are supported). -See - - - __u32 - index - Identifies the frequency band, set by the application. - - - __u32 - capability - The tuner/modulator capability flags for -this frequency band, see . The V4L2_TUNER_CAP_LOW -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. - - - __u32 - rangelow - The lowest tunable frequency in -units of 62.5 kHz, or if the capability -flag V4L2_TUNER_CAP_LOW is set, in units of 62.5 -Hz, for this frequency band. - - - __u32 - rangehigh - The highest tunable frequency in -units of 62.5 kHz, or if the capability -flag V4L2_TUNER_CAP_LOW is set, in units of 62.5 -Hz, for this frequency band. - - - __u32 - modulation - The supported modulation systems of this frequency band. - See . 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. - - - __u32 - reserved[9] - Reserved for future extensions. Applications and drivers - must set the array to zero. - - - -
- - - Band Modulation Systems - - &cs-def; - - - V4L2_BAND_MODULATION_VSB - 0x02 - Vestigial Sideband modulation, used for analog TV. - - - V4L2_BAND_MODULATION_FM - 0x04 - Frequency Modulation, commonly used for analog radio. - - - V4L2_BAND_MODULATION_AM - 0x08 - Amplitude Modulation, commonly used for analog radio. - - - -
- - - - &return-value; - - - - EINVAL - - The tuner or index -is out of bounds or the type field is wrong. - - - - - diff --git a/trunk/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml b/trunk/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml index c7a1c462e724..40e58a42eb26 100644 --- a/trunk/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml +++ b/trunk/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml @@ -98,12 +98,11 @@ the &v4l2-output; modulator field and the __u32 type The tuner type. This is the same value as in the -&v4l2-tuner; type field. The type must be set +&v4l2-tuner; type field. See The type must be set to V4L2_TUNER_RADIO for /dev/radioX device nodes, and to V4L2_TUNER_ANALOG_TV -for all others. Set this field to V4L2_TUNER_RADIO for -modulators (currently only radio modulators are supported). -See +for all others. The field is not applicable to modulators, &ie; ignored +by drivers. See __u32 diff --git a/trunk/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml b/trunk/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml index 720395127904..95d5371c1709 100644 --- a/trunk/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml +++ b/trunk/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml @@ -119,14 +119,10 @@ field is not quite clear.--> . Audio flags indicate the ability to decode audio subprograms. They will not change, for example with the current video standard.When -the structure refers to a radio tuner the -V4L2_TUNER_CAP_LANG1, -V4L2_TUNER_CAP_LANG2 and -V4L2_TUNER_CAP_NORM flags can't be used. -If multiple frequency bands are supported, then -capability is the union of all -capability> fields of each &v4l2-frequency-band;. - +the structure refers to a radio tuner only the +V4L2_TUNER_CAP_LOW, +V4L2_TUNER_CAP_STEREO and +V4L2_TUNER_CAP_RDS flags can be set. __u32 @@ -134,9 +130,7 @@ the structure refers to a radio tuner the The lowest tunable frequency in units of 62.5 kHz, or if the capability flag V4L2_TUNER_CAP_LOW is set, in units of 62.5 -Hz. If multiple frequency bands are supported, then -rangelow is the lowest frequency -of all the frequency bands. +Hz. __u32 @@ -144,9 +138,7 @@ of all the frequency bands. The highest tunable frequency in units of 62.5 kHz, or if the capability flag V4L2_TUNER_CAP_LOW is set, in units of 62.5 -Hz. If multiple frequency bands are supported, then -rangehigh is the highest frequency -of all the frequency bands. +Hz. __u32 @@ -348,12 +340,6 @@ radio tuners. 0x0200 The RDS data is parsed by the hardware and set via controls. - - V4L2_TUNER_CAP_FREQ_BANDS - 0x0400 - The &VIDIOC-ENUM-FREQ-BANDS; ioctl can be used to enumerate - the available frequency bands. - diff --git a/trunk/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/trunk/Documentation/DocBook/media/v4l/vidioc-querycap.xml index f33dd746b66b..4643505cd4ca 100644 --- a/trunk/Documentation/DocBook/media/v4l/vidioc-querycap.xml +++ b/trunk/Documentation/DocBook/media/v4l/vidioc-querycap.xml @@ -191,19 +191,6 @@ linkend="output">Video Output interface. multi-planar API through the Video Output interface. - - V4L2_CAP_VIDEO_M2M - 0x00004000 - The device supports the single-planar API through the - Video Memory-To-Memory interface. - - - V4L2_CAP_VIDEO_M2M_MPLANE - 0x00008000 - The device supports the - multi-planar API through the - Video Memory-To-Memory interface. - V4L2_CAP_VIDEO_OVERLAY 0x00000004 diff --git a/trunk/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml b/trunk/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml index 3dd1bec6d3c7..f4db44d0d95a 100644 --- a/trunk/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml +++ b/trunk/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml @@ -52,23 +52,11 @@ Start a hardware frequency seek from the current frequency. To do this applications initialize the tuner, type, seek_upward, -wrap_around, spacing, -rangelow and rangehigh -fields, and zero out the reserved array of a -&v4l2-hw-freq-seek; and call the VIDIOC_S_HW_FREQ_SEEK -ioctl with a pointer to this structure. - - The rangelow and -rangehigh fields can be set to a non-zero value to -tell the driver to search a specific band. If the &v4l2-tuner; -capability field has the -V4L2_TUNER_CAP_HWSEEK_PROG_LIM flag set, these values -must fall within one of the bands returned by &VIDIOC-ENUM-FREQ-BANDS;. If -the V4L2_TUNER_CAP_HWSEEK_PROG_LIM 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. +spacing and +wrap_around fields, and zero out the +reserved array of a &v4l2-hw-freq-seek; and +call the VIDIOC_S_HW_FREQ_SEEK ioctl with a pointer +to this structure. If an error is returned, then the original frequency will be restored. @@ -114,27 +102,7 @@ field and the &v4l2-tuner; index field. __u32 - rangelow - If non-zero, the lowest tunable frequency of the band to -search in units of 62.5 kHz, or if the &v4l2-tuner; -capability field has the -V4L2_TUNER_CAP_LOW flag set, in units of 62.5 Hz. -If rangelow is zero a reasonable default value -is used. - - - __u32 - rangehigh - If non-zero, the highest tunable frequency of the band to -search in units of 62.5 kHz, or if the &v4l2-tuner; -capability field has the -V4L2_TUNER_CAP_LOW flag set, in units of 62.5 Hz. -If rangehigh is zero a reasonable default value -is used. - - - __u32 - reserved[5] + reserved[7] Reserved for future extensions. Applications must set the array to zero. @@ -151,10 +119,8 @@ is used. EINVAL The tuner index is out of -bounds, the wrap_around value is not supported or -one of the values in the type, -rangelow or rangehigh -fields is wrong. +bounds, the wrap_around value is not supported or the value in the type field is +wrong. diff --git a/trunk/Documentation/IRQ-domain.txt b/trunk/Documentation/IRQ-domain.txt index 1401cece745a..27dcaabfb4db 100644 --- a/trunk/Documentation/IRQ-domain.txt +++ b/trunk/Documentation/IRQ-domain.txt @@ -93,7 +93,6 @@ Linux IRQ number into the hardware. Most drivers cannot use this mapping. ==== Legacy ==== -irq_domain_add_simple() irq_domain_add_legacy() irq_domain_add_legacy_isa() @@ -116,7 +115,3 @@ The legacy map should only be used if fixed IRQ mappings must be supported. For example, ISA controllers would use the legacy map for mapping Linux IRQs 0-15 so that existing ISA drivers get the correct IRQ numbers. - -Most users of legacy mappings should use irq_domain_add_simple() which -will use a legacy domain only if an IRQ range is supplied by the -system and will otherwise use a linear domain mapping. diff --git a/trunk/Documentation/block/queue-sysfs.txt b/trunk/Documentation/block/queue-sysfs.txt index 6518a55273e7..d8147b336c35 100644 --- a/trunk/Documentation/block/queue-sysfs.txt +++ b/trunk/Documentation/block/queue-sysfs.txt @@ -38,13 +38,6 @@ read or write requests. Note that the total allocated number may be twice this amount, since it applies only to reads or writes (not the accumulated sum). -To avoid priority inversion through request starvation, a request -queue maintains a separate request pool per each cgroup when -CONFIG_BLK_CGROUP is enabled, and this parameter applies to each such -per-block-cgroup request pool. IOW, if there are N block cgroups, -each request queue may have upto N request pools, each independently -regulated by nr_requests. - read_ahead_kb (RW) ------------------ Maximum number of kilobytes to read-ahead for filesystems on this block diff --git a/trunk/Documentation/cgroups/hugetlb.txt b/trunk/Documentation/cgroups/hugetlb.txt deleted file mode 100644 index a9faaca1f029..000000000000 --- a/trunk/Documentation/cgroups/hugetlb.txt +++ /dev/null @@ -1,45 +0,0 @@ -HugeTLB Controller -------------------- - -The HugeTLB controller allows to limit the HugeTLB usage per control group and -enforces the controller limit during page fault. Since HugeTLB doesn't -support page reclaim, enforcing the limit at page fault time implies that, -the application will get SIGBUS signal if it tries to access HugeTLB pages -beyond its limit. This requires the application to know beforehand how much -HugeTLB pages it would require for its use. - -HugeTLB controller can be created by first mounting the cgroup filesystem. - -# mount -t cgroup -o hugetlb none /sys/fs/cgroup - -With the above step, the initial or the parent HugeTLB group becomes -visible at /sys/fs/cgroup. At bootup, this group includes all the tasks in -the system. /sys/fs/cgroup/tasks lists the tasks in this cgroup. - -New groups can be created under the parent group /sys/fs/cgroup. - -# cd /sys/fs/cgroup -# mkdir g1 -# echo $$ > g1/tasks - -The above steps create a new group g1 and move the current shell -process (bash) into it. - -Brief summary of control files - - hugetlb..limit_in_bytes # set/show limit of "hugepagesize" hugetlb usage - hugetlb..max_usage_in_bytes # show max "hugepagesize" hugetlb usage recorded - hugetlb..usage_in_bytes # show current res_counter usage for "hugepagesize" hugetlb - hugetlb..failcnt # show the number of allocation failure due to HugeTLB limit - -For a system supporting two hugepage size (16M and 16G) the control -files include: - -hugetlb.16GB.limit_in_bytes -hugetlb.16GB.max_usage_in_bytes -hugetlb.16GB.usage_in_bytes -hugetlb.16GB.failcnt -hugetlb.16MB.limit_in_bytes -hugetlb.16MB.max_usage_in_bytes -hugetlb.16MB.usage_in_bytes -hugetlb.16MB.failcnt diff --git a/trunk/Documentation/cgroups/memory.txt b/trunk/Documentation/cgroups/memory.txt index 4372e6b8a353..dd88540bb995 100644 --- a/trunk/Documentation/cgroups/memory.txt +++ b/trunk/Documentation/cgroups/memory.txt @@ -73,8 +73,6 @@ Brief summary of control files. memory.kmem.tcp.limit_in_bytes # set/show hard limit for tcp buf memory memory.kmem.tcp.usage_in_bytes # show current tcp buf memory allocation - memory.kmem.tcp.failcnt # show the number of tcp buf memory usage hits limits - memory.kmem.tcp.max_usage_in_bytes # show max tcp buf memory usage recorded 1. History @@ -189,12 +187,12 @@ the cgroup that brought it in -- this will happen on memory pressure). But see section 8.2: when moving a task to another cgroup, its pages may be recharged to the new cgroup, if move_charge_at_immigrate has been chosen. -Exception: If CONFIG_CGROUP_CGROUP_MEMCG_SWAP is not used. +Exception: If CONFIG_CGROUP_CGROUP_MEM_RES_CTLR_SWAP is not used. When you do swapoff and make swapped-out pages of shmem(tmpfs) to be backed into memory in force, charges for pages are accounted against the caller of swapoff rather than the users of shmem. -2.4 Swap Extension (CONFIG_MEMCG_SWAP) +2.4 Swap Extension (CONFIG_CGROUP_MEM_RES_CTLR_SWAP) Swap Extension allows you to record charge for swap. A swapped-in page is charged back to original page allocator if possible. @@ -261,7 +259,7 @@ When oom event notifier is registered, event will be delivered. per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by zone->lru_lock, it has no lock of its own. -2.7 Kernel Memory Extension (CONFIG_MEMCG_KMEM) +2.7 Kernel Memory Extension (CONFIG_CGROUP_MEM_RES_CTLR_KMEM) With the Kernel memory extension, the Memory Controller is able to limit the amount of kernel memory used by the system. Kernel memory is fundamentally @@ -288,8 +286,8 @@ per cgroup, instead of globally. a. Enable CONFIG_CGROUPS b. Enable CONFIG_RESOURCE_COUNTERS -c. Enable CONFIG_MEMCG -d. Enable CONFIG_MEMCG_SWAP (to use swap extension) +c. Enable CONFIG_CGROUP_MEM_RES_CTLR +d. Enable CONFIG_CGROUP_MEM_RES_CTLR_SWAP (to use swap extension) 1. Prepare the cgroups (see cgroups.txt, Why are cgroups needed?) # mount -t tmpfs none /sys/fs/cgroup diff --git a/trunk/Documentation/device-mapper/dm-raid.txt b/trunk/Documentation/device-mapper/dm-raid.txt index 1c1844957166..946c73342cde 100644 --- a/trunk/Documentation/device-mapper/dm-raid.txt +++ b/trunk/Documentation/device-mapper/dm-raid.txt @@ -27,10 +27,6 @@ The target is named "raid" and it accepts the following parameters: - rotating parity N (right-to-left) with data restart raid6_nc RAID6 N continue - rotating parity N (right-to-left) with data continuation - raid10 Various RAID10 inspired algorithms chosen by additional params - - RAID10: Striped Mirrors (aka 'Striping on top of mirrors') - - RAID1E: Integrated Adjacent Stripe Mirroring - - and other similar RAID10 variants Reference: Chapter 4 of http://www.snia.org/sites/default/files/SNIA_DDF_Technical_Position_v2.0.pdf @@ -63,28 +59,6 @@ The target is named "raid" and it accepts the following parameters: logical size of the array. The bitmap records the device synchronisation state for each region. - [raid10_copies <# copies>] - [raid10_format near] - These two options are used to alter the default layout of - a RAID10 configuration. The number of copies is can be - specified, but the default is 2. There are other variations - to how the copies are laid down - the default and only current - option is "near". Near copies are what most people think of - with respect to mirroring. If these options are left - unspecified, or 'raid10_copies 2' and/or 'raid10_format near' - are given, then the layouts for 2, 3 and 4 devices are: - 2 drives 3 drives 4 drives - -------- ---------- -------------- - A1 A1 A1 A1 A2 A1 A1 A2 A2 - A2 A2 A2 A3 A3 A3 A3 A4 A4 - A3 A3 A4 A4 A5 A5 A5 A6 A6 - A4 A4 A5 A6 A6 A7 A7 A8 A8 - .. .. .. .. .. .. .. .. .. - The 2-device layout is equivalent 2-way RAID1. The 4-device - layout is what a traditional RAID10 would look like. The - 3-device layout is what might be called a 'RAID1E - Integrated - Adjacent Stripe Mirroring'. - <#raid_devs>: The number of devices composing the array. Each device consists of two entries. The first is the device containing the metadata (if any); the second is the one containing the diff --git a/trunk/Documentation/feature-removal-schedule.txt b/trunk/Documentation/feature-removal-schedule.txt index afaff312bf41..e9237fb71950 100644 --- a/trunk/Documentation/feature-removal-schedule.txt +++ b/trunk/Documentation/feature-removal-schedule.txt @@ -13,14 +13,6 @@ Who: Jim Cromie , Jason Baron --------------------------- -What: /proc/sys/vm/nr_pdflush_threads -When: 2012 -Why: Since pdflush is deprecated, the interface exported in /proc/sys/vm/ - should be removed. -Who: Wanpeng Li - ---------------------------- - What: CONFIG_APM_CPU_IDLE, and its ability to call APM BIOS in idle When: 2012 Why: This optional sub-feature of APM is of dubious reliability, @@ -78,6 +70,20 @@ Who: Luis R. Rodriguez --------------------------- +What: IRQF_SAMPLE_RANDOM +Check: IRQF_SAMPLE_RANDOM +When: July 2009 + +Why: Many of IRQF_SAMPLE_RANDOM users are technically bogus as entropy + sources in the kernel's current entropy model. To resolve this, every + input point to the kernel's entropy pool needs to better document the + type of entropy source it actually is. This will be replaced with + additional add_*_randomness functions in drivers/char/random.c + +Who: Robin Getz & Matt Mackall + +--------------------------- + What: The ieee80211_regdom module parameter When: March 2010 / desktop catchup @@ -612,28 +618,3 @@ Why: The regular V4L2 selections and the subdev selection API originally Who: Sylwester Nawrocki ---------------------------- - -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 - ----------------------------- - -What: OMAP private DMA implementation -When: 2013 -Why: We have a DMA engine implementation; all users should be updated - to use this rather than persisting with the old APIs. The old APIs - block merging the old DMA engine implementation into the DMA - engine driver. -Who: Russell King , - Santosh Shilimkar - ----------------------------- diff --git a/trunk/Documentation/filesystems/Locking b/trunk/Documentation/filesystems/Locking index 0f103e39b4f6..e0cce2a5f820 100644 --- a/trunk/Documentation/filesystems/Locking +++ b/trunk/Documentation/filesystems/Locking @@ -138,8 +138,8 @@ evict_inode: put_super: write write_super: read sync_fs: read -freeze_fs: write -unfreeze_fs: write +freeze_fs: read +unfreeze_fs: read statfs: maybe(read) (see below) remount_fs: write umount_begin: no @@ -206,8 +206,6 @@ prototypes: int (*launder_page)(struct page *); int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long); int (*error_remove_page)(struct address_space *, struct page *); - int (*swap_activate)(struct file *); - int (*swap_deactivate)(struct file *); locking rules: All except set_page_dirty and freepage may block @@ -231,8 +229,6 @@ migratepage: yes (both) launder_page: yes is_partially_uptodate: yes error_remove_page: yes -swap_activate: no -swap_deactivate: no ->write_begin(), ->write_end(), ->sync_page() and ->readpage() may be called from the request handler (/dev/loop). @@ -334,15 +330,6 @@ cleaned, or an error value if not. Note that in order to prevent the page getting mapped back in and redirtied, it needs to be kept locked across the entire operation. - ->swap_activate will be called with a non-zero argument on -files backing (non block device backed) swapfiles. A return value -of zero indicates success, in which case this file can be used for -backing swapspace. The swapspace operations will be proxied to the -address space operations. - - ->swap_deactivate() will be called in the sys_swapoff() -path after ->swap_activate() returned success. - ----------------------- file_lock_operations ------------------------------ prototypes: void (*fl_copy_lock)(struct file_lock *, struct file_lock *); @@ -359,6 +346,7 @@ prototypes: int (*lm_compare_owner)(struct file_lock *, struct file_lock *); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, struct file_lock *, int); + void (*lm_release_private)(struct file_lock *); void (*lm_break)(struct file_lock *); /* break_lease callback */ int (*lm_change)(struct file_lock **, int); @@ -367,6 +355,7 @@ locking rules: lm_compare_owner: yes no lm_notify: yes no lm_grant: no no +lm_release_private: maybe no lm_break: yes no lm_change yes no diff --git a/trunk/Documentation/filesystems/vfs.txt b/trunk/Documentation/filesystems/vfs.txt index 065aa2dc0835..aa754e01464e 100644 --- a/trunk/Documentation/filesystems/vfs.txt +++ b/trunk/Documentation/filesystems/vfs.txt @@ -592,8 +592,6 @@ struct address_space_operations { int (*migratepage) (struct page *, struct page *); int (*launder_page) (struct page *); int (*error_remove_page) (struct mapping *mapping, struct page *page); - int (*swap_activate)(struct file *); - int (*swap_deactivate)(struct file *); }; writepage: called by the VM to write a dirty page to backing store. @@ -762,16 +760,6 @@ struct address_space_operations { Setting this implies you deal with pages going away under you, unless you have them locked or reference counts increased. - swap_activate: Called when swapon is used on a file to allocate - space if necessary and pin the block lookup information in - memory. A return value of zero indicates success, - in which case this file can be used to back swapspace. The - swapspace operations will be proxied to this address space's - ->swap_{out,in} methods. - - swap_deactivate: Called during swapoff on files where swap_activate - was successful. - The File Object =============== diff --git a/trunk/Documentation/ioctl/ioctl-number.txt b/trunk/Documentation/ioctl/ioctl-number.txt index 849b771c5e03..915f28c470e9 100644 --- a/trunk/Documentation/ioctl/ioctl-number.txt +++ b/trunk/Documentation/ioctl/ioctl-number.txt @@ -88,7 +88,6 @@ Code Seq#(hex) Include File Comments and kernel/power/user.c '8' all SNP8023 advanced NIC card -';' 64-7F linux/vfio.h '@' 00-0F linux/radeonfb.h conflict! '@' 00-0F drivers/video/aty/aty128fb.c conflict! 'A' 00-1F linux/apm_bios.h conflict! diff --git a/trunk/Documentation/networking/ip-sysctl.txt b/trunk/Documentation/networking/ip-sysctl.txt index ca447b35b833..406a5226220d 100644 --- a/trunk/Documentation/networking/ip-sysctl.txt +++ b/trunk/Documentation/networking/ip-sysctl.txt @@ -48,6 +48,12 @@ min_adv_mss - INTEGER The advertised MSS depends on the first hop route MTU, but will never be lower than this setting. +rt_cache_rebuild_count - INTEGER + The per net-namespace route cache emergency rebuild threshold. + Any net-namespace having its route cache rebuilt due to + a hash bucket chain being too long more than this many times + will have its route caching disabled + IP Fragmentation: ipfrag_high_thresh - INTEGER diff --git a/trunk/Documentation/power/power_supply_class.txt b/trunk/Documentation/power/power_supply_class.txt index 2f0ddc15b5ac..211831d4095f 100644 --- a/trunk/Documentation/power/power_supply_class.txt +++ b/trunk/Documentation/power/power_supply_class.txt @@ -112,24 +112,14 @@ CHARGE_COUNTER - the current charge counter (in µAh). This could easily be negative; there is no empty or full value. It is only useful for relative, time-based measurements. -CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger. - -CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. - ENERGY_FULL, ENERGY_EMPTY - same as above but for energy. CAPACITY - capacity in percents. -CAPACITY_ALERT_MIN - minimum capacity alert value in percents. -CAPACITY_ALERT_MAX - maximum capacity alert value in percents. CAPACITY_LEVEL - capacity level. This corresponds to POWER_SUPPLY_CAPACITY_LEVEL_*. TEMP - temperature of the power supply. -TEMP_ALERT_MIN - minimum battery temperature alert value in milli centigrade. -TEMP_ALERT_MAX - maximum battery temperature alert value in milli centigrade. TEMP_AMBIENT - ambient temperature. -TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert value in milli centigrade. -TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert value in milli centigrade. TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e. while battery powers a load) diff --git a/trunk/Documentation/sound/alsa/HD-Audio-Models.txt b/trunk/Documentation/sound/alsa/HD-Audio-Models.txt index a92bba816843..7456360e161c 100644 --- a/trunk/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/trunk/Documentation/sound/alsa/HD-Audio-Models.txt @@ -53,7 +53,6 @@ ALC882/883/885/888/889 acer-aspire-8930g Acer Aspire 8330G/6935G acer-aspire Acer Aspire others inv-dmic Inverted internal mic workaround - no-primary-hp VAIO Z workaround (for fixed speaker DAC) ALC861/660 ========== @@ -274,10 +273,6 @@ STAC92HD83* dell-s14 Dell laptop dell-vostro-3500 Dell Vostro 3500 laptop hp-dv7-4000 HP dv-7 4000 - hp_cNB11_intquad HP CNB models with 4 speakers - hp-zephyr HP Zephyr - hp-led HP with broken BIOS for mute LED - hp-inv-led HP with broken BIOS for inverted mute LED auto BIOS setup (default) STAC9872 diff --git a/trunk/Documentation/sysctl/fs.txt b/trunk/Documentation/sysctl/fs.txt index 88152f214f48..8c235b6e4246 100644 --- a/trunk/Documentation/sysctl/fs.txt +++ b/trunk/Documentation/sysctl/fs.txt @@ -32,8 +32,6 @@ Currently, these files are in /proc/sys/fs: - nr_open - overflowuid - overflowgid -- protected_hardlinks -- protected_symlinks - suid_dumpable - super-max - super-nr @@ -159,46 +157,6 @@ The default is 65534. ============================================================== -protected_hardlinks: - -A long-standing class of security issues is the hardlink-based -time-of-check-time-of-use race, most commonly seen in world-writable -directories like /tmp. The common method of exploitation of this flaw -is to cross privilege boundaries when following a given hardlink (i.e. a -root process follows a hardlink created by another user). Additionally, -on systems without separated partitions, this stops unauthorized users -from "pinning" vulnerable setuid/setgid files against being upgraded by -the administrator, or linking to special files. - -When set to "0", hardlink creation behavior is unrestricted. - -When set to "1" hardlinks cannot be created by users if they do not -already own the source file, or do not have read/write access to it. - -This protection is based on the restrictions in Openwall and grsecurity. - -============================================================== - -protected_symlinks: - -A long-standing class of security issues is the symlink-based -time-of-check-time-of-use race, most commonly seen in world-writable -directories like /tmp. The common method of exploitation of this flaw -is to cross privilege boundaries when following a given symlink (i.e. a -root process follows a symlink belonging to another user). For a likely -incomplete list of hundreds of examples across the years, please see: -http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp - -When set to "0", symlink following behavior is unrestricted. - -When set to "1" symlinks are permitted to be followed only when outside -a sticky world-writable directory, or when the uid of the symlink and -follower match, or when the directory owner matches the symlink's owner. - -This protection is based on the restrictions in Openwall and grsecurity. - -============================================================== - suid_dumpable: This value can be used to query and set the core dump mode for setuid diff --git a/trunk/Documentation/sysctl/vm.txt b/trunk/Documentation/sysctl/vm.txt index dcc2a94ae34e..96f0ee825bed 100644 --- a/trunk/Documentation/sysctl/vm.txt +++ b/trunk/Documentation/sysctl/vm.txt @@ -42,6 +42,7 @@ Currently, these files are in /proc/sys/vm: - mmap_min_addr - nr_hugepages - nr_overcommit_hugepages +- nr_pdflush_threads - nr_trim_pages (only if CONFIG_MMU=n) - numa_zonelist_order - oom_dump_tasks @@ -425,6 +426,16 @@ See Documentation/vm/hugetlbpage.txt ============================================================== +nr_pdflush_threads + +The current number of pdflush threads. This value is read-only. +The value changes according to the number of dirty pages in the system. + +When necessary, additional pdflush threads are created, one per second, up to +nr_pdflush_threads_max. + +============================================================== + nr_trim_pages This is available only on NOMMU kernels. @@ -491,10 +502,9 @@ oom_dump_tasks Enables a system-wide task dump (excluding kernel threads) to be produced when the kernel performs an OOM-killing and includes such -information as pid, uid, tgid, vm size, rss, nr_ptes, swapents, -oom_score_adj score, and name. This is helpful to determine why the -OOM killer was invoked, to identify the rogue task that caused it, -and to determine why the OOM killer chose the task it did to kill. +information as pid, uid, tgid, vm size, rss, cpu, oom_adj score, and +name. This is helpful to determine why the OOM killer was invoked +and to identify the rogue task that caused it. If this is set to zero, this information is suppressed. On very large systems with thousands of tasks it may not be feasible to dump @@ -564,24 +574,16 @@ of physical RAM. See above. page-cluster -page-cluster controls the number of pages up to which consecutive pages -are read in from swap in a single attempt. This is the swap counterpart -to page cache readahead. -The mentioned consecutivity is not in terms of virtual/physical addresses, -but consecutive on swap space - that means they were swapped out together. +page-cluster controls the number of pages which are written to swap in +a single attempt. The swap I/O size. It is a logarithmic value - setting it to zero means "1 page", setting it to 1 means "2 pages", setting it to 2 means "4 pages", etc. -Zero disables swap readahead completely. The default value is three (eight pages at a time). There may be some small benefits in tuning this to a different value if your workload is swap-intensive. -Lower values mean lower latencies for initial faults, but at the same time -extra faults and I/O delays for following faults if they would have been part of -that consecutive pages readahead would have brought in. - ============================================================= panic_on_oom diff --git a/trunk/Documentation/vfio.txt b/trunk/Documentation/vfio.txt deleted file mode 100644 index 0cb6685c8029..000000000000 --- a/trunk/Documentation/vfio.txt +++ /dev/null @@ -1,314 +0,0 @@ -VFIO - "Virtual Function I/O"[1] -------------------------------------------------------------------------------- -Many modern system now provide DMA and interrupt remapping facilities -to help ensure I/O devices behave within the boundaries they've been -allotted. This includes x86 hardware with AMD-Vi and Intel VT-d, -POWER systems with Partitionable Endpoints (PEs) and embedded PowerPC -systems such as Freescale PAMU. The VFIO driver is an IOMMU/device -agnostic framework for exposing direct device access to userspace, in -a secure, IOMMU protected environment. In other words, this allows -safe[2], non-privileged, userspace drivers. - -Why do we want that? Virtual machines often make use of direct device -access ("device assignment") when configured for the highest possible -I/O performance. From a device and host perspective, this simply -turns the VM into a userspace driver, with the benefits of -significantly reduced latency, higher bandwidth, and direct use of -bare-metal device drivers[3]. - -Some applications, particularly in the high performance computing -field, also benefit from low-overhead, direct device access from -userspace. Examples include network adapters (often non-TCP/IP based) -and compute accelerators. Prior to VFIO, these drivers had to either -go through the full development cycle to become proper upstream -driver, be maintained out of tree, or make use of the UIO framework, -which has no notion of IOMMU protection, limited interrupt support, -and requires root privileges to access things like PCI configuration -space. - -The VFIO driver framework intends to unify these, replacing both the -KVM PCI specific device assignment code as well as provide a more -secure, more featureful userspace driver environment than UIO. - -Groups, Devices, and IOMMUs -------------------------------------------------------------------------------- - -Devices are the main target of any I/O driver. Devices typically -create a programming interface made up of I/O access, interrupts, -and DMA. Without going into the details of each of these, DMA is -by far the most critical aspect for maintaining a secure environment -as allowing a device read-write access to system memory imposes the -greatest risk to the overall system integrity. - -To help mitigate this risk, many modern IOMMUs now incorporate -isolation properties into what was, in many cases, an interface only -meant for translation (ie. solving the addressing problems of devices -with limited address spaces). With this, devices can now be isolated -from each other and from arbitrary memory access, thus allowing -things like secure direct assignment of devices into virtual machines. - -This isolation is not always at the granularity of a single device -though. Even when an IOMMU is capable of this, properties of devices, -interconnects, and IOMMU topologies can each reduce this isolation. -For instance, an individual device may be part of a larger multi- -function enclosure. While the IOMMU may be able to distinguish -between devices within the enclosure, the enclosure may not require -transactions between devices to reach the IOMMU. Examples of this -could be anything from a multi-function PCI device with backdoors -between functions to a non-PCI-ACS (Access Control Services) capable -bridge allowing redirection without reaching the IOMMU. Topology -can also play a factor in terms of hiding devices. A PCIe-to-PCI -bridge masks the devices behind it, making transaction appear as if -from the bridge itself. Obviously IOMMU design plays a major factor -as well. - -Therefore, while for the most part an IOMMU may have device level -granularity, any system is susceptible to reduced granularity. The -IOMMU API therefore supports a notion of IOMMU groups. A group is -a set of devices which is isolatable from all other devices in the -system. Groups are therefore the unit of ownership used by VFIO. - -While the group is the minimum granularity that must be used to -ensure secure user access, it's not necessarily the preferred -granularity. In IOMMUs which make use of page tables, it may be -possible to share a set of page tables between different groups, -reducing the overhead both to the platform (reduced TLB thrashing, -reduced duplicate page tables), and to the user (programming only -a single set of translations). For this reason, VFIO makes use of -a container class, which may hold one or more groups. A container -is created by simply opening the /dev/vfio/vfio character device. - -On its own, the container provides little functionality, with all -but a couple version and extension query interfaces locked away. -The user needs to add a group into the container for the next level -of functionality. To do this, the user first needs to identify the -group associated with the desired device. This can be done using -the sysfs links described in the example below. By unbinding the -device from the host driver and binding it to a VFIO driver, a new -VFIO group will appear for the group as /dev/vfio/$GROUP, where -$GROUP is the IOMMU group number of which the device is a member. -If the IOMMU group contains multiple devices, each will need to -be bound to a VFIO driver before operations on the VFIO group -are allowed (it's also sufficient to only unbind the device from -host drivers if a VFIO driver is unavailable; this will make the -group available, but not that particular device). TBD - interface -for disabling driver probing/locking a device. - -Once the group is ready, it may be added to the container by opening -the VFIO group character device (/dev/vfio/$GROUP) and using the -VFIO_GROUP_SET_CONTAINER ioctl, passing the file descriptor of the -previously opened container file. If desired and if the IOMMU driver -supports sharing the IOMMU context between groups, multiple groups may -be set to the same container. If a group fails to set to a container -with existing groups, a new empty container will need to be used -instead. - -With a group (or groups) attached to a container, the remaining -ioctls become available, enabling access to the VFIO IOMMU interfaces. -Additionally, it now becomes possible to get file descriptors for each -device within a group using an ioctl on the VFIO group file descriptor. - -The VFIO device API includes ioctls for describing the device, the I/O -regions and their read/write/mmap offsets on the device descriptor, as -well as mechanisms for describing and registering interrupt -notifications. - -VFIO Usage Example -------------------------------------------------------------------------------- - -Assume user wants to access PCI device 0000:06:0d.0 - -$ readlink /sys/bus/pci/devices/0000:06:0d.0/iommu_group -../../../../kernel/iommu_groups/26 - -This device is therefore in IOMMU group 26. This device is on the -pci bus, therefore the user will make use of vfio-pci to manage the -group: - -# modprobe vfio-pci - -Binding this device to the vfio-pci driver creates the VFIO group -character devices for this group: - -$ lspci -n -s 0000:06:0d.0 -06:0d.0 0401: 1102:0002 (rev 08) -# echo 0000:06:0d.0 > /sys/bus/pci/devices/0000:06:0d.0/driver/unbind -# echo 1102 0002 > /sys/bus/pci/drivers/vfio/new_id - -Now we need to look at what other devices are in the group to free -it for use by VFIO: - -$ ls -l /sys/bus/pci/devices/0000:06:0d.0/iommu_group/devices -total 0 -lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:00:1e.0 -> - ../../../../devices/pci0000:00/0000:00:1e.0 -lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:06:0d.0 -> - ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.0 -lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:06:0d.1 -> - ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.1 - -This device is behind a PCIe-to-PCI bridge[4], therefore we also -need to add device 0000:06:0d.1 to the group following the same -procedure as above. Device 0000:00:1e.0 is a bridge that does -not currently have a host driver, therefore it's not required to -bind this device to the vfio-pci driver (vfio-pci does not currently -support PCI bridges). - -The final step is to provide the user with access to the group if -unprivileged operation is desired (note that /dev/vfio/vfio provides -no capabilities on its own and is therefore expected to be set to -mode 0666 by the system). - -# chown user:user /dev/vfio/26 - -The user now has full access to all the devices and the iommu for this -group and can access them as follows: - - int container, group, device, i; - struct vfio_group_status group_status = - { .argsz = sizeof(group_status) }; - struct vfio_iommu_x86_info iommu_info = { .argsz = sizeof(iommu_info) }; - struct vfio_iommu_x86_dma_map dma_map = { .argsz = sizeof(dma_map) }; - struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; - - /* Create a new container */ - container = open("/dev/vfio/vfio, O_RDWR); - - if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION) - /* Unknown API version */ - - if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_X86_IOMMU)) - /* Doesn't support the IOMMU driver we want. */ - - /* Open the group */ - group = open("/dev/vfio/26", O_RDWR); - - /* Test the group is viable and available */ - ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); - - if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) - /* Group is not viable (ie, not all devices bound for vfio) */ - - /* Add the group to the container */ - ioctl(group, VFIO_GROUP_SET_CONTAINER, &container); - - /* Enable the IOMMU model we want */ - ioctl(container, VFIO_SET_IOMMU, VFIO_X86_IOMMU) - - /* Get addition IOMMU info */ - ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info); - - /* Allocate some space and setup a DMA mapping */ - dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); - dma_map.size = 1024 * 1024; - dma_map.iova = 0; /* 1MB starting at 0x0 from device view */ - dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE; - - ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); - - /* Get a file descriptor for the device */ - device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:06:0d.0"); - - /* Test and setup the device */ - ioctl(device, VFIO_DEVICE_GET_INFO, &device_info); - - for (i = 0; i < device_info.num_regions; i++) { - struct vfio_region_info reg = { .argsz = sizeof(reg) }; - - reg.index = i; - - ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®); - - /* Setup mappings... read/write offsets, mmaps - * For PCI devices, config space is a region */ - } - - for (i = 0; i < device_info.num_irqs; i++) { - struct vfio_irq_info irq = { .argsz = sizeof(irq) }; - - irq.index = i; - - ioctl(device, VFIO_DEVICE_GET_IRQ_INFO, ®); - - /* Setup IRQs... eventfds, VFIO_DEVICE_SET_IRQS */ - } - - /* Gratuitous device reset and go... */ - ioctl(device, VFIO_DEVICE_RESET); - -VFIO User API -------------------------------------------------------------------------------- - -Please see include/linux/vfio.h for complete API documentation. - -VFIO bus driver API -------------------------------------------------------------------------------- - -VFIO bus drivers, such as vfio-pci make use of only a few interfaces -into VFIO core. When devices are bound and unbound to the driver, -the driver should call vfio_add_group_dev() and vfio_del_group_dev() -respectively: - -extern int vfio_add_group_dev(struct iommu_group *iommu_group, - struct device *dev, - const struct vfio_device_ops *ops, - void *device_data); - -extern void *vfio_del_group_dev(struct device *dev); - -vfio_add_group_dev() indicates to the core to begin tracking the -specified iommu_group and register the specified dev as owned by -a VFIO bus driver. The driver provides an ops structure for callbacks -similar to a file operations structure: - -struct vfio_device_ops { - int (*open)(void *device_data); - void (*release)(void *device_data); - ssize_t (*read)(void *device_data, char __user *buf, - size_t count, loff_t *ppos); - ssize_t (*write)(void *device_data, const char __user *buf, - size_t size, loff_t *ppos); - long (*ioctl)(void *device_data, unsigned int cmd, - unsigned long arg); - int (*mmap)(void *device_data, struct vm_area_struct *vma); -}; - -Each function is passed the device_data that was originally registered -in the vfio_add_group_dev() call above. This allows the bus driver -an easy place to store its opaque, private data. The open/release -callbacks are issued when a new file descriptor is created for a -device (via VFIO_GROUP_GET_DEVICE_FD). The ioctl interface provides -a direct pass through for VFIO_DEVICE_* ioctls. The read/write/mmap -interfaces implement the device region access defined by the device's -own VFIO_DEVICE_GET_REGION_INFO ioctl. - -------------------------------------------------------------------------------- - -[1] VFIO was originally an acronym for "Virtual Function I/O" in its -initial implementation by Tom Lyon while as Cisco. We've since -outgrown the acronym, but it's catchy. - -[2] "safe" also depends upon a device being "well behaved". It's -possible for multi-function devices to have backdoors between -functions and even for single function devices to have alternative -access to things like PCI config space through MMIO registers. To -guard against the former we can include additional precautions in the -IOMMU driver to group multi-function PCI devices together -(iommu=group_mf). The latter we can't prevent, but the IOMMU should -still provide isolation. For PCI, SR-IOV Virtual Functions are the -best indicator of "well behaved", as these are designed for -virtualization usage models. - -[3] As always there are trade-offs to virtual machine device -assignment that are beyond the scope of VFIO. It's expected that -future IOMMU technologies will reduce some, but maybe not all, of -these trade-offs. - -[4] In this case the device is below a PCI bridge, so transactions -from either function of the device are indistinguishable to the iommu: - --[0000:00]-+-1e.0-[06]--+-0d.0 - \-0d.1 - -00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev 90) diff --git a/trunk/Documentation/video4linux/CARDLIST.au0828 b/trunk/Documentation/video4linux/CARDLIST.au0828 index a8a65753e544..7b59e953c4bf 100644 --- a/trunk/Documentation/video4linux/CARDLIST.au0828 +++ b/trunk/Documentation/video4linux/CARDLIST.au0828 @@ -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) [05e1:0480,2040:8200] + 5 -> Hauppauge Woodbury (au0828) [2040:8200] diff --git a/trunk/Documentation/video4linux/CARDLIST.bttv b/trunk/Documentation/video4linux/CARDLIST.bttv index 581f666a76cf..b753906c7183 100644 --- a/trunk/Documentation/video4linux/CARDLIST.bttv +++ b/trunk/Documentation/video4linux/CARDLIST.bttv @@ -159,4 +159,3 @@ 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] diff --git a/trunk/Documentation/video4linux/CARDLIST.cx23885 b/trunk/Documentation/video4linux/CARDLIST.cx23885 index 652aecd13199..f316d1816fcd 100644 --- a/trunk/Documentation/video4linux/CARDLIST.cx23885 +++ b/trunk/Documentation/video4linux/CARDLIST.cx23885 @@ -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:22f1] + 20 -> Hauppauge WinTV-HVR1255 [0070:2251,0070:2259,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,5 +33,3 @@ 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] diff --git a/trunk/Documentation/video4linux/CARDLIST.saa7134 b/trunk/Documentation/video4linux/CARDLIST.saa7134 index 94d9025aa82d..34f3b330e5f4 100644 --- a/trunk/Documentation/video4linux/CARDLIST.saa7134 +++ b/trunk/Documentation/video4linux/CARDLIST.saa7134 @@ -188,4 +188,3 @@ 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] diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 94b823f71e94..fb036a062a5d 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -1789,16 +1789,15 @@ F: arch/powerpc/oprofile/*cell* F: arch/powerpc/platforms/cell/ CEPH DISTRIBUTED FILE SYSTEM CLIENT -M: Sage Weil +M: Sage Weil L: ceph-devel@vger.kernel.org -W: http://ceph.com/ +W: http://ceph.newdream.net/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git S: Supported F: Documentation/filesystems/ceph.txt F: fs/ceph F: net/ceph F: include/linux/ceph -F: include/linux/crush CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM: L: linux-usb@vger.kernel.org @@ -5640,12 +5639,10 @@ S: Supported F: arch/hexagon/ RADOS BLOCK DEVICE (RBD) -M: Yehuda Sadeh -M: Sage Weil -M: Alex Elder +F: include/linux/qnxtypes.h +M: Yehuda Sadeh +M: Sage Weil M: ceph-devel@vger.kernel.org -W: http://ceph.com/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git S: Supported F: drivers/block/rbd.c F: drivers/block/rbd_types.h @@ -5682,7 +5679,7 @@ F: Documentation/blockdev/ramdisk.txt F: drivers/block/brd.c RANDOM NUMBER DRIVER -M: Theodore Ts'o" +M: Matt Mackall S: Maintained F: drivers/char/random.c @@ -7382,7 +7379,6 @@ W: http://user-mode-linux.sourceforge.net S: Maintained F: Documentation/virtual/uml/ F: arch/um/ -F: arch/x86/um/ F: fs/hostfs/ F: fs/hppfs/ @@ -7415,14 +7411,6 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ -VFIO DRIVER -M: Alex Williamson -L: kvm@vger.kernel.org -S: Maintained -F: Documentation/vfio.txt -F: drivers/vfio/ -F: include/linux/vfio.h - VIDEOBUF2 FRAMEWORK M: Pawel Osciak M: Marek Szyprowski diff --git a/trunk/arch/arm/configs/omap2plus_defconfig b/trunk/arch/arm/configs/omap2plus_defconfig index e58edc36b406..b152de79fd95 100644 --- a/trunk/arch/arm/configs/omap2plus_defconfig +++ b/trunk/arch/arm/configs/omap2plus_defconfig @@ -193,8 +193,6 @@ CONFIG_MMC_OMAP_HS=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_TWL92330=y CONFIG_RTC_DRV_TWL4030=y -CONFIG_DMADEVICES=y -CONFIG_DMA_OMAP=y CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y # CONFIG_EXT3_FS_XATTR is not set diff --git a/trunk/arch/arm/include/asm/cacheflush.h b/trunk/arch/arm/include/asm/cacheflush.h index e4448e16046d..004c1bc95d2b 100644 --- a/trunk/arch/arm/include/asm/cacheflush.h +++ b/trunk/arch/arm/include/asm/cacheflush.h @@ -215,9 +215,7 @@ static inline void vivt_flush_cache_mm(struct mm_struct *mm) static inline void vivt_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { - struct mm_struct *mm = vma->vm_mm; - - if (!mm || cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm))) + if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) __cpuc_flush_user_range(start & PAGE_MASK, PAGE_ALIGN(end), vma->vm_flags); } @@ -225,9 +223,7 @@ vivt_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned static inline void vivt_flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn) { - struct mm_struct *mm = vma->vm_mm; - - if (!mm || cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm))) { + if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) { unsigned long addr = user_addr & PAGE_MASK; __cpuc_flush_user_range(addr, addr + PAGE_SIZE, vma->vm_flags); } diff --git a/trunk/arch/arm/include/asm/mutex.h b/trunk/arch/arm/include/asm/mutex.h index b1479fd04a95..93226cf23ae0 100644 --- a/trunk/arch/arm/include/asm/mutex.h +++ b/trunk/arch/arm/include/asm/mutex.h @@ -7,10 +7,121 @@ */ #ifndef _ASM_MUTEX_H #define _ASM_MUTEX_H + +#if __LINUX_ARM_ARCH__ < 6 +/* On pre-ARMv6 hardware the swp based implementation is the most efficient. */ +# include +#else + /* - * On pre-ARMv6 hardware this results in a swp-based implementation, - * which is the most efficient. For ARMv6+, we emit a pair of exclusive - * accesses instead. + * Attempting to lock a mutex on ARMv6+ can be done with a bastardized + * atomic decrement (it is not a reliable atomic decrement but it satisfies + * the defined semantics for our purpose, while being smaller and faster + * than a real atomic decrement or atomic swap. The idea is to attempt + * decrementing the lock value only once. If once decremented it isn't zero, + * or if its store-back fails due to a dispute on the exclusive store, we + * simply bail out immediately through the slow path where the lock will be + * reattempted until it succeeds. */ -#include +static inline void +__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *)) +{ + int __ex_flag, __res; + + __asm__ ( + + "ldrex %0, [%2] \n\t" + "sub %0, %0, #1 \n\t" + "strex %1, %0, [%2] " + + : "=&r" (__res), "=&r" (__ex_flag) + : "r" (&(count)->counter) + : "cc","memory" ); + + __res |= __ex_flag; + if (unlikely(__res != 0)) + fail_fn(count); +} + +static inline int +__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *)) +{ + int __ex_flag, __res; + + __asm__ ( + + "ldrex %0, [%2] \n\t" + "sub %0, %0, #1 \n\t" + "strex %1, %0, [%2] " + + : "=&r" (__res), "=&r" (__ex_flag) + : "r" (&(count)->counter) + : "cc","memory" ); + + __res |= __ex_flag; + if (unlikely(__res != 0)) + __res = fail_fn(count); + return __res; +} + +/* + * Same trick is used for the unlock fast path. However the original value, + * rather than the result, is used to test for success in order to have + * better generated assembly. + */ +static inline void +__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *)) +{ + int __ex_flag, __res, __orig; + + __asm__ ( + + "ldrex %0, [%3] \n\t" + "add %1, %0, #1 \n\t" + "strex %2, %1, [%3] " + + : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag) + : "r" (&(count)->counter) + : "cc","memory" ); + + __orig |= __ex_flag; + if (unlikely(__orig != 0)) + fail_fn(count); +} + +/* + * If the unlock was done on a contended lock, or if the unlock simply fails + * then the mutex remains locked. + */ +#define __mutex_slowpath_needs_to_unlock() 1 + +/* + * For __mutex_fastpath_trylock we use another construct which could be + * described as a "single value cmpxchg". + * + * This provides the needed trylock semantics like cmpxchg would, but it is + * lighter and less generic than a true cmpxchg implementation. + */ +static inline int +__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *)) +{ + int __ex_flag, __res, __orig; + + __asm__ ( + + "1: ldrex %0, [%3] \n\t" + "subs %1, %0, #1 \n\t" + "strexeq %2, %1, [%3] \n\t" + "movlt %0, #0 \n\t" + "cmpeq %2, #0 \n\t" + "bgt 1b " + + : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag) + : "r" (&count->counter) + : "cc", "memory" ); + + return __orig; +} + +#endif #endif diff --git a/trunk/arch/arm/include/asm/setup.h b/trunk/arch/arm/include/asm/setup.h index 24d284a1bfc7..23ebc0c82a39 100644 --- a/trunk/arch/arm/include/asm/setup.h +++ b/trunk/arch/arm/include/asm/setup.h @@ -196,7 +196,7 @@ static const struct tagtable __tagtable_##fn __tag = { tag, fn } struct membank { phys_addr_t start; - phys_addr_t size; + unsigned long size; unsigned int highmem; }; @@ -217,7 +217,7 @@ extern struct meminfo meminfo; #define bank_phys_end(bank) ((bank)->start + (bank)->size) #define bank_phys_size(bank) (bank)->size -extern int arm_add_memory(phys_addr_t start, phys_addr_t size); +extern int arm_add_memory(phys_addr_t start, unsigned long size); extern void early_print(const char *str, ...); extern void dump_machine_table(void); diff --git a/trunk/arch/arm/kernel/entry-armv.S b/trunk/arch/arm/kernel/entry-armv.S index 0f82098c9bfe..0d1851ca6eb9 100644 --- a/trunk/arch/arm/kernel/entry-armv.S +++ b/trunk/arch/arm/kernel/entry-armv.S @@ -244,19 +244,6 @@ svc_preempt: b 1b #endif -__und_fault: - @ Correct the PC such that it is pointing at the instruction - @ which caused the fault. If the faulting instruction was ARM - @ the PC will be pointing at the next instruction, and have to - @ subtract 4. Otherwise, it is Thumb, and the PC will be - @ pointing at the second half of the Thumb instruction. We - @ have to subtract 2. - ldr r2, [r0, #S_PC] - sub r2, r2, r1 - str r2, [r0, #S_PC] - b do_undefinstr -ENDPROC(__und_fault) - .align 5 __und_svc: #ifdef CONFIG_KPROBES @@ -274,32 +261,25 @@ __und_svc: @ @ r0 - instruction @ -#ifndef CONFIG_THUMB2_KERNEL +#ifndef CONFIG_THUMB2_KERNEL ldr r0, [r4, #-4] #else - mov r1, #2 ldrh r0, [r4, #-2] @ Thumb instruction at LR - 2 cmp r0, #0xe800 @ 32-bit instruction if xx >= 0 - blo __und_svc_fault - ldrh r9, [r4] @ bottom 16 bits - add r4, r4, #2 - str r4, [sp, #S_PC] - orr r0, r9, r0, lsl #16 + ldrhhs r9, [r4] @ bottom 16 bits + orrhs r0, r9, r0, lsl #16 #endif - adr r9, BSYM(__und_svc_finish) + adr r9, BSYM(1f) mov r2, r4 bl call_fpe - mov r1, #4 @ PC correction to apply -__und_svc_fault: mov r0, sp @ struct pt_regs *regs - bl __und_fault + bl do_undefinstr @ @ IRQs off again before pulling preserved data off the stack @ -__und_svc_finish: - disable_irq_notrace +1: disable_irq_notrace @ @ restore SPSR and restart the instruction @@ -443,33 +423,25 @@ __und_usr: mov r2, r4 mov r3, r5 - @ r2 = regs->ARM_pc, which is either 2 or 4 bytes ahead of the - @ faulting instruction depending on Thumb mode. - @ r3 = regs->ARM_cpsr @ - @ The emulation code returns using r9 if it has emulated the - @ instruction, or the more conventional lr if we are to treat - @ this as a real undefined instruction + @ fall through to the emulation code, which returns using r9 if + @ it has emulated the instruction, or the more conventional lr + @ if we are to treat this as a real undefined instruction + @ + @ r0 - instruction @ adr r9, BSYM(ret_from_exception) - + adr lr, BSYM(__und_usr_unknown) tst r3, #PSR_T_BIT @ Thumb mode? - bne __und_usr_thumb - sub r4, r2, #4 @ ARM instr at LR - 4 -1: ldrt r0, [r4] + itet eq @ explicit IT needed for the 1f label + subeq r4, r2, #4 @ ARM instr at LR - 4 + subne r4, r2, #2 @ Thumb instr at LR - 2 +1: ldreqt r0, [r4] #ifdef CONFIG_CPU_ENDIAN_BE8 - rev r0, r0 @ little endian instruction + reveq r0, r0 @ little endian instruction #endif - @ r0 = 32-bit ARM instruction which caused the exception - @ r2 = PC value for the following instruction (:= regs->ARM_pc) - @ r4 = PC value for the faulting instruction - @ lr = 32-bit undefined instruction function - adr lr, BSYM(__und_usr_fault_32) - b call_fpe - -__und_usr_thumb: + beq call_fpe @ Thumb instruction - sub r4, r2, #2 @ First half of thumb instr at LR - 2 #if CONFIG_ARM_THUMB && __LINUX_ARM_ARCH__ >= 6 && CONFIG_CPU_V7 /* * Thumb-2 instruction handling. Note that because pre-v6 and >= v6 platforms @@ -483,7 +455,7 @@ __und_usr_thumb: ldr r5, .LCcpu_architecture ldr r5, [r5] cmp r5, #CPU_ARCH_ARMv7 - blo __und_usr_fault_16 @ 16bit undefined instruction + blo __und_usr_unknown /* * The following code won't get run unless the running CPU really is v7, so * coding round the lack of ldrht on older arches is pointless. Temporarily @@ -491,18 +463,15 @@ __und_usr_thumb: */ .arch armv6t2 #endif -2: ldrht r5, [r4] +2: + ARM( ldrht r5, [r4], #2 ) + THUMB( ldrht r5, [r4] ) + THUMB( add r4, r4, #2 ) cmp r5, #0xe800 @ 32bit instruction if xx != 0 - blo __und_usr_fault_16 @ 16bit undefined instruction -3: ldrht r0, [r2] + blo __und_usr_unknown +3: ldrht r0, [r4] add r2, r2, #2 @ r2 is PC + 2, make it PC + 4 - str r2, [sp, #S_PC] @ it's a 2x16bit instr, update orr r0, r0, r5, lsl #16 - adr lr, BSYM(__und_usr_fault_32) - @ r0 = the two 16-bit Thumb instructions which caused the exception - @ r2 = PC value for the following Thumb instruction (:= regs->ARM_pc) - @ r4 = PC value for the first 16-bit Thumb instruction - @ lr = 32bit undefined instruction function #if __LINUX_ARM_ARCH__ < 7 /* If the target arch was overridden, change it back: */ @@ -513,13 +482,17 @@ __und_usr_thumb: #endif #endif /* __LINUX_ARM_ARCH__ < 7 */ #else /* !(CONFIG_ARM_THUMB && __LINUX_ARM_ARCH__ >= 6 && CONFIG_CPU_V7) */ - b __und_usr_fault_16 + b __und_usr_unknown #endif - UNWIND(.fnend) + UNWIND(.fnend ) ENDPROC(__und_usr) + @ + @ fallthrough to call_fpe + @ + /* - * The out of line fixup for the ldrt instructions above. + * The out of line fixup for the ldrt above. */ .pushsection .fixup, "ax" .align 2 @@ -551,12 +524,11 @@ ENDPROC(__und_usr) * NEON handler code. * * Emulators may wish to make use of the following registers: - * r0 = instruction opcode (32-bit ARM or two 16-bit Thumb) - * r2 = PC value to resume execution after successful emulation + * r0 = instruction opcode. + * r2 = PC+4 * r9 = normal "successful" return address - * r10 = this threads thread_info structure + * r10 = this threads thread_info structure. * lr = unrecognised instruction return address - * IRQs disabled, FIQs enabled. */ @ @ Fall-through from Thumb-2 __und_usr @@ -687,17 +659,12 @@ ENTRY(no_fp) mov pc, lr ENDPROC(no_fp) -__und_usr_fault_32: - mov r1, #4 - b 1f -__und_usr_fault_16: - mov r1, #2 -1: enable_irq +__und_usr_unknown: + enable_irq mov r0, sp adr lr, BSYM(ret_from_exception) - b __und_fault -ENDPROC(__und_usr_fault_32) -ENDPROC(__und_usr_fault_16) + b do_undefinstr +ENDPROC(__und_usr_unknown) .align 5 __pabt_usr: diff --git a/trunk/arch/arm/kernel/entry-common.S b/trunk/arch/arm/kernel/entry-common.S index 978eac57e04a..49d9f9305247 100644 --- a/trunk/arch/arm/kernel/entry-common.S +++ b/trunk/arch/arm/kernel/entry-common.S @@ -51,15 +51,23 @@ ret_fast_syscall: fast_work_pending: str r0, [sp, #S_R0+S_OFF]! @ returned r0 work_pending: + tst r1, #_TIF_NEED_RESCHED + bne work_resched + /* + * TIF_SIGPENDING or TIF_NOTIFY_RESUME must've been set if we got here + */ + ldr r2, [sp, #S_PSR] mov r0, sp @ 'regs' + tst r2, #15 @ are we returning to user mode? + bne no_work_pending @ no? just leave, then... mov r2, why @ 'syscall' - bl do_work_pending - cmp r0, #0 - beq no_work_pending - movlt scno, #(__NR_restart_syscall - __NR_SYSCALL_BASE) - ldmia sp, {r0 - r6} @ have to reload r0 - r6 - b local_restart @ ... and off we go + tst r1, #_TIF_SIGPENDING @ delivering a signal? + movne why, #0 @ prevent further restarts + bl do_notify_resume + b ret_slow_syscall @ Check work again +work_resched: + bl schedule /* * "slow" syscall return path. "why" tells us if this was a real syscall. */ @@ -401,7 +409,6 @@ ENTRY(vector_swi) eor scno, scno, #__NR_SYSCALL_BASE @ check OS number #endif -local_restart: ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing stmdb sp!, {r4, r5} @ push fifth and sixth args @@ -443,8 +450,7 @@ __sys_trace: mov scno, r0 @ syscall number (possibly new) add r1, sp, #S_R0 + S_OFF @ pointer to regs cmp scno, #NR_syscalls @ check upper syscall limit - ldmccia r1, {r0 - r6} @ have to reload r0 - r6 - stmccia sp, {r4, r5} @ and update the stack args + ldmccia r1, {r0 - r3} @ have to reload r0 - r3 ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine b 2b diff --git a/trunk/arch/arm/kernel/ftrace.c b/trunk/arch/arm/kernel/ftrace.c index 34e56647dcee..df0bf0c8cb79 100644 --- a/trunk/arch/arm/kernel/ftrace.c +++ b/trunk/arch/arm/kernel/ftrace.c @@ -179,21 +179,20 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, old = *parent; *parent = return_hooker; - trace.func = self_addr; - trace.depth = current->curr_ret_stack + 1; - - /* Only trace if the calling function expects to */ - if (!ftrace_graph_entry(&trace)) { - *parent = old; - return; - } - err = ftrace_push_return_trace(old, self_addr, &trace.depth, frame_pointer); if (err == -EBUSY) { *parent = old; return; } + + trace.func = self_addr; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + current->curr_ret_stack--; + *parent = old; + } } #ifdef CONFIG_DYNAMIC_FTRACE diff --git a/trunk/arch/arm/kernel/process.c b/trunk/arch/arm/kernel/process.c index 693b744fd572..19c95ea65b2f 100644 --- a/trunk/arch/arm/kernel/process.c +++ b/trunk/arch/arm/kernel/process.c @@ -247,7 +247,6 @@ void machine_shutdown(void) void machine_halt(void) { machine_shutdown(); - local_irq_disable(); while (1); } @@ -269,7 +268,6 @@ void machine_restart(char *cmd) /* Whoops - the platform was unable to reboot. Tell the user! */ printk("Reboot failed -- System halted\n"); - local_irq_disable(); while (1); } diff --git a/trunk/arch/arm/kernel/ptrace.c b/trunk/arch/arm/kernel/ptrace.c index 3e0fc5f7ed4b..dab711e6e1ca 100644 --- a/trunk/arch/arm/kernel/ptrace.c +++ b/trunk/arch/arm/kernel/ptrace.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/trunk/arch/arm/kernel/setup.c b/trunk/arch/arm/kernel/setup.c index a81dcecc7343..e15d83bb4ea3 100644 --- a/trunk/arch/arm/kernel/setup.c +++ b/trunk/arch/arm/kernel/setup.c @@ -508,7 +508,7 @@ void __init dump_machine_table(void) /* can't use cpu_relax() here as it may require MMU setup */; } -int __init arm_add_memory(phys_addr_t start, phys_addr_t size) +int __init arm_add_memory(phys_addr_t start, unsigned long size) { struct membank *bank = &meminfo.bank[meminfo.nr_banks]; @@ -538,7 +538,7 @@ int __init arm_add_memory(phys_addr_t start, phys_addr_t size) } #endif - bank->size = size & ~(phys_addr_t)(PAGE_SIZE - 1); + bank->size = size & PAGE_MASK; /* * Check whether this memory region has non-zero size or @@ -558,7 +558,7 @@ int __init arm_add_memory(phys_addr_t start, phys_addr_t size) static int __init early_mem(char *p) { static int usermem __initdata = 0; - phys_addr_t size; + unsigned long size; phys_addr_t start; char *endp; diff --git a/trunk/arch/arm/kernel/signal.c b/trunk/arch/arm/kernel/signal.c index f27789e4e38a..536c5d6b340b 100644 --- a/trunk/arch/arm/kernel/signal.c +++ b/trunk/arch/arm/kernel/signal.c @@ -27,6 +27,7 @@ */ #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE)) #define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE)) +#define SWI_SYS_RESTART (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE) /* * With EABI, the syscall number has to be loaded into r7. @@ -46,6 +47,18 @@ const unsigned long sigreturn_codes[7] = { MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, }; +/* + * Either we support OABI only, or we have EABI with the OABI + * compat layer enabled. In the later case we don't know if + * user space is EABI or not, and if not we must not clobber r7. + * Always using the OABI syscall solves that issue and works for + * all those cases. + */ +const unsigned long syscall_restart_code[2] = { + SWI_SYS_RESTART, /* swi __NR_restart_syscall */ + 0xe49df004, /* ldr pc, [sp], #4 */ +}; + /* * atomically swap in the new signal mask, and wait for a signal. */ @@ -569,13 +582,12 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -static int do_signal(struct pt_regs *regs, int syscall) +static void do_signal(struct pt_regs *regs, int syscall) { unsigned int retval = 0, continue_addr = 0, restart_addr = 0; struct k_sigaction ka; siginfo_t info; int signr; - int restart = 0; /* * If we were from a system call, check for system call restarting... @@ -590,15 +602,15 @@ static int do_signal(struct pt_regs *regs, int syscall) * debugger will see the already changed PSW. */ switch (retval) { - case -ERESTART_RESTARTBLOCK: - restart -= 2; case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: - restart++; regs->ARM_r0 = regs->ARM_ORIG_r0; regs->ARM_pc = restart_addr; break; + case -ERESTART_RESTARTBLOCK: + regs->ARM_r0 = -EINTR; + break; } } @@ -607,17 +619,14 @@ static int do_signal(struct pt_regs *regs, int syscall) * point the debugger may change all our registers ... */ signr = get_signal_to_deliver(&info, &ka, regs, NULL); - /* - * Depending on the signal settings we may need to revert the - * decision to restart the system call. But skip this if a - * debugger has chosen to restart at a different PC. - */ - if (regs->ARM_pc != restart_addr) - restart = 0; if (signr > 0) { - if (unlikely(restart)) { - if (retval == -ERESTARTNOHAND || - retval == -ERESTART_RESTARTBLOCK + /* + * Depending on the signal settings we may need to revert the + * decision to restart the system call. But skip this if a + * debugger has chosen to restart at a different PC. + */ + if (regs->ARM_pc == restart_addr) { + if (retval == -ERESTARTNOHAND || (retval == -ERESTARTSYS && !(ka.sa.sa_flags & SA_RESTART))) { regs->ARM_r0 = -EINTR; @@ -626,43 +635,52 @@ static int do_signal(struct pt_regs *regs, int syscall) } handle_signal(signr, &ka, &info, regs); - return 0; + return; + } + + if (syscall) { + /* + * Handle restarting a different system call. As above, + * if a debugger has chosen to restart at a different PC, + * ignore the restart. + */ + if (retval == -ERESTART_RESTARTBLOCK + && regs->ARM_pc == continue_addr) { + if (thumb_mode(regs)) { + regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; + regs->ARM_pc -= 2; + } else { +#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT) + regs->ARM_r7 = __NR_restart_syscall; + regs->ARM_pc -= 4; +#else + u32 __user *usp; + + regs->ARM_sp -= 4; + usp = (u32 __user *)regs->ARM_sp; + + if (put_user(regs->ARM_pc, usp) == 0) { + regs->ARM_pc = KERN_RESTART_CODE; + } else { + regs->ARM_sp += 4; + force_sigsegv(0, current); + } +#endif + } + } } restore_saved_sigmask(); - if (unlikely(restart)) - regs->ARM_pc = continue_addr; - return restart; } -asmlinkage int -do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) +asmlinkage void +do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) { - do { - if (likely(thread_flags & _TIF_NEED_RESCHED)) { - schedule(); - } else { - if (unlikely(!user_mode(regs))) - return 0; - local_irq_enable(); - if (thread_flags & _TIF_SIGPENDING) { - int restart = do_signal(regs, syscall); - if (unlikely(restart)) { - /* - * Restart without handlers. - * Deal with it without leaving - * the kernel space. - */ - return restart; - } - syscall = 0; - } else { - clear_thread_flag(TIF_NOTIFY_RESUME); - tracehook_notify_resume(regs); - } - } - local_irq_disable(); - thread_flags = current_thread_info()->flags; - } while (thread_flags & _TIF_WORK_MASK); - return 0; + if (thread_flags & _TIF_SIGPENDING) + do_signal(regs, syscall); + + if (thread_flags & _TIF_NOTIFY_RESUME) { + clear_thread_flag(TIF_NOTIFY_RESUME); + tracehook_notify_resume(regs); + } } diff --git a/trunk/arch/arm/kernel/signal.h b/trunk/arch/arm/kernel/signal.h index 5ff067b7c752..6fcfe8398aa4 100644 --- a/trunk/arch/arm/kernel/signal.h +++ b/trunk/arch/arm/kernel/signal.h @@ -8,5 +8,7 @@ * published by the Free Software Foundation. */ #define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500) +#define KERN_RESTART_CODE (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes)) extern const unsigned long sigreturn_codes[7]; +extern const unsigned long syscall_restart_code[2]; diff --git a/trunk/arch/arm/kernel/smp.c b/trunk/arch/arm/kernel/smp.c index ebd8ad274d76..aea74f5bc34a 100644 --- a/trunk/arch/arm/kernel/smp.c +++ b/trunk/arch/arm/kernel/smp.c @@ -563,8 +563,7 @@ void smp_send_stop(void) cpumask_copy(&mask, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), &mask); - if (!cpumask_empty(&mask)) - smp_cross_call(&mask, IPI_CPU_STOP); + smp_cross_call(&mask, IPI_CPU_STOP); /* Wait up to one second for other CPUs to stop */ timeout = USEC_PER_SEC; diff --git a/trunk/arch/arm/kernel/traps.c b/trunk/arch/arm/kernel/traps.c index f7945218b8c6..8b97d739b17b 100644 --- a/trunk/arch/arm/kernel/traps.c +++ b/trunk/arch/arm/kernel/traps.c @@ -402,10 +402,18 @@ static int call_undef_hook(struct pt_regs *regs, unsigned int instr) asmlinkage void __exception do_undefinstr(struct pt_regs *regs) { + unsigned int correction = thumb_mode(regs) ? 2 : 4; unsigned int instr; siginfo_t info; void __user *pc; + /* + * According to the ARM ARM, PC is 2 or 4 bytes ahead, + * depending whether we're in Thumb mode or not. + * Correct this offset. + */ + regs->ARM_pc -= correction; + pc = (void __user *)instruction_pointer(regs); if (processor_mode(regs) == SVC_MODE) { @@ -844,6 +852,8 @@ void __init early_trap_init(void *vectors_base) */ memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE), sigreturn_codes, sizeof(sigreturn_codes)); + memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE), + syscall_restart_code, sizeof(syscall_restart_code)); flush_icache_range(vectors, vectors + PAGE_SIZE); modify_domain(DOMAIN_USER, DOMAIN_CLIENT); diff --git a/trunk/arch/arm/mach-davinci/devices-da8xx.c b/trunk/arch/arm/mach-davinci/devices-da8xx.c index 783eab6845c4..d1624a315c9a 100644 --- a/trunk/arch/arm/mach-davinci/devices-da8xx.c +++ b/trunk/arch/arm/mach-davinci/devices-da8xx.c @@ -546,7 +546,6 @@ static struct lcd_ctrl_config lcd_cfg = { .sync_edge = 0, .sync_ctrl = 1, .raster_order = 0, - .fifo_th = 6, }; struct da8xx_lcdc_platform_data sharp_lcd035q3dg01_pdata = { diff --git a/trunk/arch/arm/mach-omap1/board-h2-mmc.c b/trunk/arch/arm/mach-omap1/board-h2-mmc.c index e1362ce48497..da0e37d40823 100644 --- a/trunk/arch/arm/mach-omap1/board-h2-mmc.c +++ b/trunk/arch/arm/mach-omap1/board-h2-mmc.c @@ -54,6 +54,7 @@ static struct omap_mmc_platform_data mmc1_data = { .nr_slots = 1, .init = mmc_late_init, .cleanup = mmc_cleanup, + .dma_mask = 0xffffffff, .slots[0] = { .set_power = mmc_set_power, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, diff --git a/trunk/arch/arm/mach-omap1/board-h3-mmc.c b/trunk/arch/arm/mach-omap1/board-h3-mmc.c index c74daace8cd6..f8242aa9b763 100644 --- a/trunk/arch/arm/mach-omap1/board-h3-mmc.c +++ b/trunk/arch/arm/mach-omap1/board-h3-mmc.c @@ -36,6 +36,7 @@ static int mmc_set_power(struct device *dev, int slot, int power_on, */ static struct omap_mmc_platform_data mmc1_data = { .nr_slots = 1, + .dma_mask = 0xffffffff, .slots[0] = { .set_power = mmc_set_power, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, diff --git a/trunk/arch/arm/mach-omap1/board-nokia770.c b/trunk/arch/arm/mach-omap1/board-nokia770.c index 2c0ca8fc3380..4007a372481b 100644 --- a/trunk/arch/arm/mach-omap1/board-nokia770.c +++ b/trunk/arch/arm/mach-omap1/board-nokia770.c @@ -185,6 +185,7 @@ static int nokia770_mmc_get_cover_state(struct device *dev, int slot) static struct omap_mmc_platform_data nokia770_mmc2_data = { .nr_slots = 1, + .dma_mask = 0xffffffff, .max_freq = 12000000, .slots[0] = { .set_power = nokia770_mmc_set_power, diff --git a/trunk/arch/arm/mach-omap1/board-palmz71.c b/trunk/arch/arm/mach-omap1/board-palmz71.c index 355980321c2d..cc71a26723ef 100644 --- a/trunk/arch/arm/mach-omap1/board-palmz71.c +++ b/trunk/arch/arm/mach-omap1/board-palmz71.c @@ -288,7 +288,8 @@ palmz71_gpio_setup(int early) } gpio_direction_input(PALMZ71_USBDETECT_GPIO); if (request_irq(gpio_to_irq(PALMZ71_USBDETECT_GPIO), - palmz71_powercable, 0, "palmz71-cable", NULL)) + palmz71_powercable, IRQF_SAMPLE_RANDOM, + "palmz71-cable", NULL)) printk(KERN_ERR "IRQ request for power cable failed!\n"); palmz71_powercable(gpio_to_irq(PALMZ71_USBDETECT_GPIO), NULL); diff --git a/trunk/arch/arm/mach-omap2/board-n8x0.c b/trunk/arch/arm/mach-omap2/board-n8x0.c index 677357ff61ac..2c5d0ed75285 100644 --- a/trunk/arch/arm/mach-omap2/board-n8x0.c +++ b/trunk/arch/arm/mach-omap2/board-n8x0.c @@ -468,6 +468,7 @@ static struct omap_mmc_platform_data mmc1_data = { .cleanup = n8x0_mmc_cleanup, .shutdown = n8x0_mmc_shutdown, .max_freq = 24000000, + .dma_mask = 0xffffffff, .slots[0] = { .wires = 4, .set_power = n8x0_mmc_set_power, diff --git a/trunk/arch/arm/mach-omap2/display.c b/trunk/arch/arm/mach-omap2/display.c index af1ed7d24a1f..5fb47a14f4ba 100644 --- a/trunk/arch/arm/mach-omap2/display.c +++ b/trunk/arch/arm/mach-omap2/display.c @@ -37,7 +37,6 @@ #define DISPC_CONTROL 0x0040 #define DISPC_CONTROL2 0x0238 -#define DISPC_CONTROL3 0x0848 #define DISPC_IRQSTATUS 0x0018 #define DSS_SYSCONFIG 0x10 @@ -53,7 +52,6 @@ #define EVSYNC_EVEN_IRQ_SHIFT 2 #define EVSYNC_ODD_IRQ_SHIFT 3 #define FRAMEDONE2_IRQ_SHIFT 22 -#define FRAMEDONE3_IRQ_SHIFT 30 #define FRAMEDONETV_IRQ_SHIFT 24 /* @@ -378,7 +376,7 @@ int __init omap_display_init(struct omap_dss_board_info *board_data) static void dispc_disable_outputs(void) { u32 v, irq_mask = 0; - bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false; + bool lcd_en, digit_en, lcd2_en = false; int i; struct omap_dss_dispc_dev_attr *da; struct omap_hwmod *oh; @@ -407,13 +405,7 @@ static void dispc_disable_outputs(void) lcd2_en = v & LCD_EN_MASK; } - /* store value of LCDENABLE for LCD3 */ - if (da->manager_count > 3) { - v = omap_hwmod_read(oh, DISPC_CONTROL3); - lcd3_en = v & LCD_EN_MASK; - } - - if (!(lcd_en | digit_en | lcd2_en | lcd3_en)) + if (!(lcd_en | digit_en | lcd2_en)) return; /* no managers currently enabled */ /* @@ -434,12 +426,10 @@ static void dispc_disable_outputs(void) if (lcd2_en) irq_mask |= 1 << FRAMEDONE2_IRQ_SHIFT; - if (lcd3_en) - irq_mask |= 1 << FRAMEDONE3_IRQ_SHIFT; /* * clear any previous FRAMEDONE, FRAMEDONETV, - * EVSYNC_EVEN/ODD, FRAMEDONE2 or FRAMEDONE3 interrupts + * EVSYNC_EVEN/ODD or FRAMEDONE2 interrupts */ omap_hwmod_write(irq_mask, oh, DISPC_IRQSTATUS); @@ -455,19 +445,12 @@ static void dispc_disable_outputs(void) omap_hwmod_write(v, oh, DISPC_CONTROL2); } - /* disable LCD3 manager */ - if (da->manager_count > 3) { - v = omap_hwmod_read(oh, DISPC_CONTROL3); - v &= ~LCD_EN_MASK; - omap_hwmod_write(v, oh, DISPC_CONTROL3); - } - i = 0; while ((omap_hwmod_read(oh, DISPC_IRQSTATUS) & irq_mask) != irq_mask) { i++; if (i > FRAMEDONE_IRQ_TIMEOUT) { - pr_err("didn't get FRAMEDONE1/2/3 or TV interrupt\n"); + pr_err("didn't get FRAMEDONE1/2 or TV interrupt\n"); break; } mdelay(1); diff --git a/trunk/arch/arm/mach-omap2/hsmmc.c b/trunk/arch/arm/mach-omap2/hsmmc.c index a9675d8d1822..be697d4e0843 100644 --- a/trunk/arch/arm/mach-omap2/hsmmc.c +++ b/trunk/arch/arm/mach-omap2/hsmmc.c @@ -315,6 +315,7 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, mmc->slots[0].caps = c->caps; mmc->slots[0].pm_caps = c->pm_caps; mmc->slots[0].internal_clock = !c->ext_clock; + mmc->dma_mask = 0xffffffff; mmc->max_freq = c->max_freq; if (cpu_is_omap44xx()) mmc->reg_offset = OMAP4_MMC_REG_OFFSET; diff --git a/trunk/arch/arm/mach-pxa/lubbock.c b/trunk/arch/arm/mach-pxa/lubbock.c index 0ca0db787903..6bb3f47b1f14 100644 --- a/trunk/arch/arm/mach-pxa/lubbock.c +++ b/trunk/arch/arm/mach-pxa/lubbock.c @@ -456,7 +456,7 @@ static int lubbock_mci_init(struct device *dev, init_timer(&mmc_timer); mmc_timer.data = (unsigned long) data; return request_irq(LUBBOCK_SD_IRQ, lubbock_detect_int, - 0, "lubbock-sd-detect", data); + IRQF_SAMPLE_RANDOM, "lubbock-sd-detect", data); } static int lubbock_mci_get_ro(struct device *dev) diff --git a/trunk/arch/arm/mach-pxa/magician.c b/trunk/arch/arm/mach-pxa/magician.c index 39561dcf65f2..2db697cd2b4e 100644 --- a/trunk/arch/arm/mach-pxa/magician.c +++ b/trunk/arch/arm/mach-pxa/magician.c @@ -633,8 +633,9 @@ static struct platform_device bq24022 = { static int magician_mci_init(struct device *dev, irq_handler_t detect_irq, void *data) { - return request_irq(IRQ_MAGICIAN_SD, detect_irq, IRQF_DISABLED, - "mmc card detect", data); + return request_irq(IRQ_MAGICIAN_SD, detect_irq, + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + "mmc card detect", data); } static void magician_mci_exit(struct device *dev, void *data) diff --git a/trunk/arch/arm/mach-pxa/trizeps4.c b/trunk/arch/arm/mach-pxa/trizeps4.c index 166dd32cc1d3..2b6ac00b2cd9 100644 --- a/trunk/arch/arm/mach-pxa/trizeps4.c +++ b/trunk/arch/arm/mach-pxa/trizeps4.c @@ -332,8 +332,8 @@ static int trizeps4_mci_init(struct device *dev, irq_handler_t mci_detect_int, int err; err = request_irq(TRIZEPS4_MMC_IRQ, mci_detect_int, - IRQF_DISABLED | IRQF_TRIGGER_RISING, - "MMC card detect", data); + IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_SAMPLE_RANDOM, + "MMC card detect", data); if (err) { printk(KERN_ERR "trizeps4_mci_init: MMC/SD: can't request" "MMC card detect IRQ\n"); diff --git a/trunk/arch/arm/mach-spear3xx/spear300.c b/trunk/arch/arm/mach-spear3xx/spear300.c index 6ec300549960..0f882ecb7d81 100644 --- a/trunk/arch/arm/mach-spear3xx/spear300.c +++ b/trunk/arch/arm/mach-spear3xx/spear300.c @@ -120,156 +120,182 @@ struct pl08x_channel_data spear300_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, }; diff --git a/trunk/arch/arm/mach-spear3xx/spear310.c b/trunk/arch/arm/mach-spear3xx/spear310.c index 1d0e435b9045..bbcf4571d361 100644 --- a/trunk/arch/arm/mach-spear3xx/spear310.c +++ b/trunk/arch/arm/mach-spear3xx/spear310.c @@ -205,156 +205,182 @@ struct pl08x_channel_data spear310_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart2_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart2_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart3_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart3_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart4_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart4_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart5_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart5_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, }; diff --git a/trunk/arch/arm/mach-spear3xx/spear320.c b/trunk/arch/arm/mach-spear3xx/spear320.c index fd823c624575..88d483bcd66a 100644 --- a/trunk/arch/arm/mach-spear3xx/spear320.c +++ b/trunk/arch/arm/mach-spear3xx/spear320.c @@ -213,156 +213,182 @@ struct pl08x_channel_data spear320_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c0_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c0_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp1_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp1_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart1_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart1_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart2_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart2_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c1_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c1_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c2_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c2_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2s_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2s_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "rs485_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "rs485_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB2, }, }; diff --git a/trunk/arch/arm/mach-spear3xx/spear3xx.c b/trunk/arch/arm/mach-spear3xx/spear3xx.c index 98144baf8883..66db5f13af84 100644 --- a/trunk/arch/arm/mach-spear3xx/spear3xx.c +++ b/trunk/arch/arm/mach-spear3xx/spear3xx.c @@ -46,8 +46,7 @@ struct pl022_ssp_controller pl022_plat_data = { struct pl08x_platform_data pl080_plat_data = { .memcpy_channel = { .bus_id = "memcpy", - .cctl_memcpy = - (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ + .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \ diff --git a/trunk/arch/arm/mach-spear6xx/spear6xx.c b/trunk/arch/arm/mach-spear6xx/spear6xx.c index 5a5a52db252b..9af67d003c62 100644 --- a/trunk/arch/arm/mach-spear6xx/spear6xx.c +++ b/trunk/arch/arm/mach-spear6xx/spear6xx.c @@ -36,288 +36,336 @@ static struct pl08x_channel_data spear600_dma_info[] = { .min_signal = 0, .max_signal = 0, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp1_tx", .min_signal = 1, .max_signal = 1, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_rx", .min_signal = 2, .max_signal = 2, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_rx", .min_signal = 4, .max_signal = 4, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_tx", .min_signal = 5, .max_signal = 5, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp2_rx", .min_signal = 6, .max_signal = 6, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_tx", .min_signal = 7, .max_signal = 7, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, + .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ext0_rx", .min_signal = 0, .max_signal = 0, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext0_tx", .min_signal = 1, .max_signal = 1, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext1_rx", .min_signal = 2, .max_signal = 2, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext1_tx", .min_signal = 3, .max_signal = 3, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext2_rx", .min_signal = 4, .max_signal = 4, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext2_tx", .min_signal = 5, .max_signal = 5, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext3_rx", .min_signal = 6, .max_signal = 6, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext3_tx", .min_signal = 7, .max_signal = 7, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext4_rx", .min_signal = 8, .max_signal = 8, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext4_tx", .min_signal = 9, .max_signal = 9, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext5_rx", .min_signal = 10, .max_signal = 10, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext5_tx", .min_signal = 11, .max_signal = 11, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext6_rx", .min_signal = 12, .max_signal = 12, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext6_tx", .min_signal = 13, .max_signal = 13, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext7_rx", .min_signal = 14, .max_signal = 14, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext7_tx", .min_signal = 15, .max_signal = 15, .muxval = 2, + .cctl = 0, .periph_buses = PL08X_AHB2, }, }; @@ -325,8 +373,7 @@ static struct pl08x_channel_data spear600_dma_info[] = { struct pl08x_platform_data pl080_plat_data = { .memcpy_channel = { .bus_id = "memcpy", - .cctl_memcpy = - (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ + .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \ diff --git a/trunk/arch/arm/mm/tlb-v7.S b/trunk/arch/arm/mm/tlb-v7.S index c2021139cb56..845f461f8ec1 100644 --- a/trunk/arch/arm/mm/tlb-v7.S +++ b/trunk/arch/arm/mm/tlb-v7.S @@ -38,19 +38,11 @@ ENTRY(v7wbi_flush_user_tlb_range) dsb mov r0, r0, lsr #PAGE_SHIFT @ align address mov r1, r1, lsr #PAGE_SHIFT -#ifdef CONFIG_ARM_ERRATA_720789 - mov r3, #0 -#else asid r3, r3 @ mask ASID -#endif orr r0, r3, r0, lsl #PAGE_SHIFT @ Create initial MVA mov r1, r1, lsl #PAGE_SHIFT 1: -#ifdef CONFIG_ARM_ERRATA_720789 - ALT_SMP(mcr p15, 0, r0, c8, c3, 3) @ TLB invalidate U MVA all ASID (shareable) -#else ALT_SMP(mcr p15, 0, r0, c8, c3, 1) @ TLB invalidate U MVA (shareable) -#endif ALT_UP(mcr p15, 0, r0, c8, c7, 1) @ TLB invalidate U MVA add r0, r0, #PAGE_SZ @@ -75,11 +67,7 @@ ENTRY(v7wbi_flush_kern_tlb_range) mov r0, r0, lsl #PAGE_SHIFT mov r1, r1, lsl #PAGE_SHIFT 1: -#ifdef CONFIG_ARM_ERRATA_720789 - ALT_SMP(mcr p15, 0, r0, c8, c3, 3) @ TLB invalidate U MVA all ASID (shareable) -#else ALT_SMP(mcr p15, 0, r0, c8, c3, 1) @ TLB invalidate U MVA (shareable) -#endif ALT_UP(mcr p15, 0, r0, c8, c7, 1) @ TLB invalidate U MVA add r0, r0, #PAGE_SZ cmp r0, r1 diff --git a/trunk/arch/arm/plat-omap/include/plat/mmc.h b/trunk/arch/arm/plat-omap/include/plat/mmc.h index eb3e4d555343..5493bd95da5e 100644 --- a/trunk/arch/arm/plat-omap/include/plat/mmc.h +++ b/trunk/arch/arm/plat-omap/include/plat/mmc.h @@ -81,6 +81,8 @@ struct omap_mmc_platform_data { /* Return context loss count due to PM states changing */ int (*get_context_loss_count)(struct device *dev); + u64 dma_mask; + /* Integrating attributes from the omap_hwmod layer */ u8 controller_flags; diff --git a/trunk/arch/arm/plat-spear/include/plat/pl080.h b/trunk/arch/arm/plat-spear/include/plat/pl080.h index eb6590ded40d..2bc6b54460a8 100644 --- a/trunk/arch/arm/plat-spear/include/plat/pl080.h +++ b/trunk/arch/arm/plat-spear/include/plat/pl080.h @@ -14,8 +14,8 @@ #ifndef __PLAT_PL080_H #define __PLAT_PL080_H -struct pl08x_channel_data; -int pl080_get_signal(const struct pl08x_channel_data *cd); -void pl080_put_signal(const struct pl08x_channel_data *cd, int signal); +struct pl08x_dma_chan; +int pl080_get_signal(struct pl08x_dma_chan *ch); +void pl080_put_signal(struct pl08x_dma_chan *ch); #endif /* __PLAT_PL080_H */ diff --git a/trunk/arch/arm/plat-spear/pl080.c b/trunk/arch/arm/plat-spear/pl080.c index cfa1199d0f4a..12cf27f935f9 100644 --- a/trunk/arch/arm/plat-spear/pl080.c +++ b/trunk/arch/arm/plat-spear/pl080.c @@ -27,8 +27,9 @@ struct { unsigned char val; } signals[16] = {{0, 0}, }; -int pl080_get_signal(const struct pl08x_channel_data *cd) +int pl080_get_signal(struct pl08x_dma_chan *ch) { + const struct pl08x_channel_data *cd = ch->cd; unsigned int signal = cd->min_signal, val; unsigned long flags; @@ -62,17 +63,18 @@ int pl080_get_signal(const struct pl08x_channel_data *cd) return signal; } -void pl080_put_signal(const struct pl08x_channel_data *cd, int signal) +void pl080_put_signal(struct pl08x_dma_chan *ch) { + const struct pl08x_channel_data *cd = ch->cd; unsigned long flags; spin_lock_irqsave(&lock, flags); /* if signal is not used */ - if (!signals[signal].busy) + if (!signals[cd->min_signal].busy) BUG(); - signals[signal].busy--; + signals[cd->min_signal].busy--; spin_unlock_irqrestore(&lock, flags); } diff --git a/trunk/arch/arm/vfp/entry.S b/trunk/arch/arm/vfp/entry.S index cc926c985981..4fa9903b83cf 100644 --- a/trunk/arch/arm/vfp/entry.S +++ b/trunk/arch/arm/vfp/entry.S @@ -7,20 +7,18 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * Basic entry code, called from the kernel's undefined instruction trap. + * r0 = faulted instruction + * r5 = faulted PC+4 + * r9 = successful return + * r10 = thread_info structure + * lr = failure return */ #include #include #include "../kernel/entry-header.S" -@ VFP entry point. -@ -@ r0 = instruction opcode (32-bit ARM or two 16-bit Thumb) -@ r2 = PC value to resume execution after successful emulation -@ r9 = normal "successful" return address -@ r10 = this threads thread_info structure -@ lr = unrecognised instruction return address -@ IRQs disabled. -@ ENTRY(do_vfp) #ifdef CONFIG_PREEMPT ldr r4, [r10, #TI_PREEMPT] @ get preempt count diff --git a/trunk/arch/arm/vfp/vfphw.S b/trunk/arch/arm/vfp/vfphw.S index ea0349f63586..d50f0e486cf2 100644 --- a/trunk/arch/arm/vfp/vfphw.S +++ b/trunk/arch/arm/vfp/vfphw.S @@ -62,13 +62,13 @@ @ VFP hardware support entry point. @ -@ r0 = instruction opcode (32-bit ARM or two 16-bit Thumb) -@ r2 = PC value to resume execution after successful emulation -@ r9 = normal "successful" return address +@ r0 = faulted instruction +@ r2 = faulted PC+4 +@ r9 = successful return @ r10 = vfp_state union @ r11 = CPU number -@ lr = unrecognised instruction return address -@ IRQs enabled. +@ lr = failure return + ENTRY(vfp_support_entry) DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10 @@ -162,12 +162,9 @@ vfp_hw_state_valid: @ exception before retrying branch @ out before setting an FPEXC that @ stops us reading stuff - VFPFMXR FPEXC, r1 @ Restore FPEXC last - sub r2, r2, #4 @ Retry current instruction - if Thumb - str r2, [sp, #S_PC] @ mode it's two 16-bit instructions, - @ else it's one 32-bit instruction, so - @ always subtract 4 from the following - @ instruction address. + VFPFMXR FPEXC, r1 @ restore FPEXC last + sub r2, r2, #4 + str r2, [sp, #S_PC] @ retry the instruction #ifdef CONFIG_PREEMPT get_thread_info r10 ldr r4, [r10, #TI_PREEMPT] @ get preempt count diff --git a/trunk/arch/arm/vfp/vfpmodule.c b/trunk/arch/arm/vfp/vfpmodule.c index fb849d044bde..586961929e96 100644 --- a/trunk/arch/arm/vfp/vfpmodule.c +++ b/trunk/arch/arm/vfp/vfpmodule.c @@ -457,16 +457,10 @@ static int vfp_pm_suspend(void) /* disable, just in case */ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); - } else if (vfp_current_hw_state[ti->cpu]) { -#ifndef CONFIG_SMP - fmxr(FPEXC, fpexc | FPEXC_EN); - vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc); - fmxr(FPEXC, fpexc); -#endif } /* clear any information we had about last context state */ - vfp_current_hw_state[ti->cpu] = NULL; + memset(vfp_current_hw_state, 0, sizeof(vfp_current_hw_state)); return 0; } diff --git a/trunk/arch/ia64/kernel/irq_ia64.c b/trunk/arch/ia64/kernel/irq_ia64.c index 1034884b77da..5c3e0888265a 100644 --- a/trunk/arch/ia64/kernel/irq_ia64.c +++ b/trunk/arch/ia64/kernel/irq_ia64.c @@ -23,6 +23,7 @@ #include #include #include +#include /* for rand_initialize_irq() */ #include #include #include diff --git a/trunk/arch/ia64/kernel/perfmon.c b/trunk/arch/ia64/kernel/perfmon.c index 3fa4bc536953..d7f558c1e711 100644 --- a/trunk/arch/ia64/kernel/perfmon.c +++ b/trunk/arch/ia64/kernel/perfmon.c @@ -2353,6 +2353,7 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t */ insert_vm_struct(mm, vma); + mm->total_vm += size >> PAGE_SHIFT; vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, vma_pages(vma)); up_write(&task->mm->mmap_sem); diff --git a/trunk/arch/mips/lantiq/xway/sysctrl.c b/trunk/arch/mips/lantiq/xway/sysctrl.c index 83780f7c842b..befbb760ab76 100644 --- a/trunk/arch/mips/lantiq/xway/sysctrl.c +++ b/trunk/arch/mips/lantiq/xway/sysctrl.c @@ -20,10 +20,12 @@ /* clock control register */ #define CGU_IFCCR 0x0018 +#define CGU_IFCCR_VR9 0x0024 /* system clock register */ #define CGU_SYS 0x0010 /* pci control register */ #define CGU_PCICR 0x0034 +#define CGU_PCICR_VR9 0x0038 /* ephy configuration register */ #define CGU_EPHY 0x10 /* power control register */ @@ -80,6 +82,9 @@ static void __iomem *pmu_membase; void __iomem *ltq_cgu_membase; void __iomem *ltq_ebu_membase; +static u32 ifccr = CGU_IFCCR; +static u32 pcicr = CGU_PCICR; + /* legacy function kept alive to ease clkdev transition */ void ltq_pmu_enable(unsigned int module) { @@ -103,14 +108,14 @@ EXPORT_SYMBOL(ltq_pmu_disable); /* enable a hw clock */ static int cgu_enable(struct clk *clk) { - ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | clk->bits, CGU_IFCCR); + ltq_cgu_w32(ltq_cgu_r32(ifccr) | clk->bits, ifccr); return 0; } /* disable a hw clock */ static void cgu_disable(struct clk *clk) { - ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~clk->bits, CGU_IFCCR); + ltq_cgu_w32(ltq_cgu_r32(ifccr) & ~clk->bits, ifccr); } /* enable a clock gate */ @@ -138,22 +143,22 @@ static void pmu_disable(struct clk *clk) /* the pci enable helper */ static int pci_enable(struct clk *clk) { - unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR); + unsigned int val = ltq_cgu_r32(ifccr); /* set bus clock speed */ if (of_machine_is_compatible("lantiq,ar9")) { - ifccr &= ~0x1f00000; + val &= ~0x1f00000; if (clk->rate == CLOCK_33M) - ifccr |= 0xe00000; + val |= 0xe00000; else - ifccr |= 0x700000; /* 62.5M */ + val |= 0x700000; /* 62.5M */ } else { - ifccr &= ~0xf00000; + val &= ~0xf00000; if (clk->rate == CLOCK_33M) - ifccr |= 0x800000; + val |= 0x800000; else - ifccr |= 0x400000; /* 62.5M */ + val |= 0x400000; /* 62.5M */ } - ltq_cgu_w32(ifccr, CGU_IFCCR); + ltq_cgu_w32(val, ifccr); pmu_enable(clk); return 0; } @@ -161,18 +166,16 @@ static int pci_enable(struct clk *clk) /* enable the external clock as a source */ static int pci_ext_enable(struct clk *clk) { - ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~(1 << 16), - CGU_IFCCR); - ltq_cgu_w32((1 << 30), CGU_PCICR); + ltq_cgu_w32(ltq_cgu_r32(ifccr) & ~(1 << 16), ifccr); + ltq_cgu_w32((1 << 30), pcicr); return 0; } /* disable the external clock as a source */ static void pci_ext_disable(struct clk *clk) { - ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | (1 << 16), - CGU_IFCCR); - ltq_cgu_w32((1 << 31) | (1 << 30), CGU_PCICR); + ltq_cgu_w32(ltq_cgu_r32(ifccr) | (1 << 16), ifccr); + ltq_cgu_w32((1 << 31) | (1 << 30), pcicr); } /* enable a clockout source */ @@ -184,11 +187,11 @@ static int clkout_enable(struct clk *clk) for (i = 0; i < 4; i++) { if (clk->rates[i] == clk->rate) { int shift = 14 - (2 * clk->module); - unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR); + unsigned int val = ltq_cgu_r32(ifccr); - ifccr &= ~(3 << shift); - ifccr |= i << shift; - ltq_cgu_w32(ifccr, CGU_IFCCR); + val &= ~(3 << shift); + val |= i << shift; + ltq_cgu_w32(val, ifccr); return 0; } } @@ -336,8 +339,12 @@ void __init ltq_soc_init(void) clkdev_add_clkout(); /* add the soc dependent clocks */ - if (!of_machine_is_compatible("lantiq,vr9")) + if (of_machine_is_compatible("lantiq,vr9")) { + ifccr = CGU_IFCCR_VR9; + pcicr = CGU_PCICR_VR9; + } else { clkdev_add_pmu("1e180000.etop", NULL, 0, PMU_PPE); + } if (!of_machine_is_compatible("lantiq,ase")) { clkdev_add_pmu("1e100c00.serial", NULL, 0, PMU_ASC1); diff --git a/trunk/arch/mips/sgi-ip27/ip27-memory.c b/trunk/arch/mips/sgi-ip27/ip27-memory.c index cd8fcab6b054..b105eca3c020 100644 --- a/trunk/arch/mips/sgi-ip27/ip27-memory.c +++ b/trunk/arch/mips/sgi-ip27/ip27-memory.c @@ -401,7 +401,6 @@ static void __init node_mem_init(cnodeid_t node) * Allocate the node data structures on the node first. */ __node_data[node] = __va(slot_freepfn << PAGE_SHIFT); - memset(__node_data[node], 0, PAGE_SIZE); NODE_DATA(node)->bdata = &bootmem_node_data[node]; NODE_DATA(node)->node_start_pfn = start_pfn; diff --git a/trunk/arch/powerpc/boot/dts/p2020rdb-pc_32b.dts b/trunk/arch/powerpc/boot/dts/p2020rdb-pc_32b.dts index 57573bd52caa..852e5b27485d 100644 --- a/trunk/arch/powerpc/boot/dts/p2020rdb-pc_32b.dts +++ b/trunk/arch/powerpc/boot/dts/p2020rdb-pc_32b.dts @@ -56,7 +56,7 @@ ranges = <0x0 0x0 0xffe00000 0x100000>; }; - pci2: pcie@ffe08000 { + pci0: pcie@ffe08000 { reg = <0 0xffe08000 0 0x1000>; status = "disabled"; }; @@ -76,7 +76,7 @@ }; }; - pci0: pcie@ffe0a000 { + pci2: pcie@ffe0a000 { reg = <0 0xffe0a000 0 0x1000>; ranges = <0x2000000 0x0 0xe0000000 0 0x80000000 0x0 0x20000000 0x1000000 0x0 0x00000000 0 0xffc00000 0x0 0x10000>; diff --git a/trunk/arch/powerpc/boot/dts/p2020rdb-pc_36b.dts b/trunk/arch/powerpc/boot/dts/p2020rdb-pc_36b.dts index 470247ea68b4..b5a56ca51cf7 100644 --- a/trunk/arch/powerpc/boot/dts/p2020rdb-pc_36b.dts +++ b/trunk/arch/powerpc/boot/dts/p2020rdb-pc_36b.dts @@ -56,7 +56,7 @@ ranges = <0x0 0xf 0xffe00000 0x100000>; }; - pci2: pcie@fffe08000 { + pci0: pcie@fffe08000 { reg = <0xf 0xffe08000 0 0x1000>; status = "disabled"; }; @@ -76,7 +76,7 @@ }; }; - pci0: pcie@fffe0a000 { + pci2: pcie@fffe0a000 { reg = <0xf 0xffe0a000 0 0x1000>; ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x20000000 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; diff --git a/trunk/arch/powerpc/boot/dts/p3041ds.dts b/trunk/arch/powerpc/boot/dts/p3041ds.dts index 6cdcadc80c30..22a215e94162 100644 --- a/trunk/arch/powerpc/boot/dts/p3041ds.dts +++ b/trunk/arch/powerpc/boot/dts/p3041ds.dts @@ -58,7 +58,7 @@ #size-cells = <1>; compatible = "spansion,s25sl12801"; reg = <0>; - spi-max-frequency = <35000000>; /* input clock */ + spi-max-frequency = <40000000>; /* input clock */ partition@u-boot { label = "u-boot"; reg = <0x00000000 0x00100000>; diff --git a/trunk/arch/powerpc/configs/chroma_defconfig b/trunk/arch/powerpc/configs/chroma_defconfig index 29bb11ec6c64..b1f9597fe312 100644 --- a/trunk/arch/powerpc/configs/chroma_defconfig +++ b/trunk/arch/powerpc/configs/chroma_defconfig @@ -21,8 +21,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y -CONFIG_CGROUP_MEMCG_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_NAMESPACES=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y diff --git a/trunk/arch/powerpc/kvm/book3s_rmhandlers.S b/trunk/arch/powerpc/kvm/book3s_rmhandlers.S index 9ecf6e35cd8d..ab523f3c1731 100644 --- a/trunk/arch/powerpc/kvm/book3s_rmhandlers.S +++ b/trunk/arch/powerpc/kvm/book3s_rmhandlers.S @@ -67,6 +67,7 @@ kvmppc_skip_Hinterrupt: #elif defined(CONFIG_PPC_BOOK3S_32) #define FUNC(name) name +#define MTMSR_EERI(reg) mtmsr (reg) .macro INTERRUPT_TRAMPOLINE intno diff --git a/trunk/arch/powerpc/platforms/85xx/p1022_ds.c b/trunk/arch/powerpc/platforms/85xx/p1022_ds.c index 3c732acf331d..89ee02c54561 100644 --- a/trunk/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/trunk/arch/powerpc/platforms/85xx/p1022_ds.c @@ -208,7 +208,6 @@ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port) u8 __iomem *lbc_lcs0_ba = NULL; u8 __iomem *lbc_lcs1_ba = NULL; phys_addr_t cs0_addr, cs1_addr; - u32 br0, or0, br1, or1; const __be32 *iprop; unsigned int num_laws; u8 b; @@ -257,70 +256,11 @@ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port) } num_laws = be32_to_cpup(iprop); - /* - * Indirect mode requires both BR0 and BR1 to be set to "GPCM", - * otherwise writes to these addresses won't actually appear on the - * local bus, and so the PIXIS won't see them. - * - * In FCM mode, writes go to the NAND controller, which does not pass - * them to the localbus directly. So we force BR0 and BR1 into GPCM - * mode, since we don't care about what's behind the localbus any - * more. - */ - br0 = in_be32(&lbc->bank[0].br); - br1 = in_be32(&lbc->bank[1].br); - or0 = in_be32(&lbc->bank[0].or); - or1 = in_be32(&lbc->bank[1].or); - - /* Make sure CS0 and CS1 are programmed */ - if (!(br0 & BR_V) || !(br1 & BR_V)) { - pr_err("p1022ds: CS0 and/or CS1 is not programmed\n"); - goto exit; - } - - /* - * Use the existing BRx/ORx values if it's already GPCM. Otherwise, - * force the values to simple 32KB GPCM windows with the most - * conservative timing. - */ - if ((br0 & BR_MSEL) != BR_MS_GPCM) { - br0 = (br0 & BR_BA) | BR_V; - or0 = 0xFFFF8000 | 0xFF7; - out_be32(&lbc->bank[0].br, br0); - out_be32(&lbc->bank[0].or, or0); - } - if ((br1 & BR_MSEL) != BR_MS_GPCM) { - br1 = (br1 & BR_BA) | BR_V; - or1 = 0xFFFF8000 | 0xFF7; - out_be32(&lbc->bank[1].br, br1); - out_be32(&lbc->bank[1].or, or1); - } - - cs0_addr = lbc_br_to_phys(ecm, num_laws, br0); - if (!cs0_addr) { - pr_err("p1022ds: could not determine physical address for CS0" - " (BR0=%08x)\n", br0); - goto exit; - } - cs1_addr = lbc_br_to_phys(ecm, num_laws, br1); - if (!cs0_addr) { - pr_err("p1022ds: could not determine physical address for CS1" - " (BR1=%08x)\n", br1); - goto exit; - } + cs0_addr = lbc_br_to_phys(ecm, num_laws, in_be32(&lbc->bank[0].br)); + cs1_addr = lbc_br_to_phys(ecm, num_laws, in_be32(&lbc->bank[1].br)); lbc_lcs0_ba = ioremap(cs0_addr, 1); - if (!lbc_lcs0_ba) { - pr_err("p1022ds: could not ioremap CS0 address %llx\n", - (unsigned long long)cs0_addr); - goto exit; - } lbc_lcs1_ba = ioremap(cs1_addr, 1); - if (!lbc_lcs1_ba) { - pr_err("p1022ds: could not ioremap CS1 address %llx\n", - (unsigned long long)cs1_addr); - goto exit; - } /* Make sure we're in indirect mode first. */ if ((in_be32(&guts->pmuxcr) & PMUXCR_ELBCDIU_MASK) != @@ -479,6 +419,18 @@ void __init p1022_ds_pic_init(void) #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) +/* + * Disables a node in the device tree. + * + * This function is called before kmalloc() is available, so the 'new' object + * should be allocated in the global area. The easiest way is to do that is + * to allocate one static local variable for each call to this function. + */ +static void __init disable_one_node(struct device_node *np, struct property *new) +{ + prom_update_property(np, new); +} + /* TRUE if there is a "video=fslfb" command-line parameter. */ static bool fslfb; @@ -541,58 +493,28 @@ static void __init p1022_ds_setup_arch(void) diu_ops.valid_monitor_port = p1022ds_valid_monitor_port; /* - * Disable the NOR and NAND flash nodes if there is video=fslfb... - * command-line parameter. When the DIU is active, the localbus is - * unavailable, so we have to disable these nodes before the MTD - * driver loads. + * Disable the NOR flash node if there is video=fslfb... command-line + * parameter. When the DIU is active, NOR flash is unavailable, so we + * have to disable the node before the MTD driver loads. */ if (fslfb) { struct device_node *np = of_find_compatible_node(NULL, NULL, "fsl,p1022-elbc"); if (np) { - struct device_node *np2; - - of_node_get(np); - np2 = of_find_compatible_node(np, NULL, "cfi-flash"); - if (np2) { + np = of_find_compatible_node(np, NULL, "cfi-flash"); + if (np) { static struct property nor_status = { .name = "status", .value = "disabled", .length = sizeof("disabled"), }; - /* - * prom_update_property() is called before - * kmalloc() is available, so the 'new' object - * should be allocated in the global area. - * The easiest way is to do that is to - * allocate one static local variable for each - * call to this function. - */ pr_info("p1022ds: disabling %s node", - np2->full_name); - prom_update_property(np2, &nor_status); - of_node_put(np2); + np->full_name); + disable_one_node(np, &nor_status); + of_node_put(np); } - - of_node_get(np); - np2 = of_find_compatible_node(np, NULL, - "fsl,elbc-fcm-nand"); - if (np2) { - static struct property nand_status = { - .name = "status", - .value = "disabled", - .length = sizeof("disabled"), - }; - - pr_info("p1022ds: disabling %s node", - np2->full_name); - prom_update_property(np2, &nand_status); - of_node_put(np2); - } - - of_node_put(np); } } diff --git a/trunk/arch/powerpc/platforms/cell/spufs/inode.c b/trunk/arch/powerpc/platforms/cell/spufs/inode.c index dba1ce235da5..d544d7816df3 100644 --- a/trunk/arch/powerpc/platforms/cell/spufs/inode.c +++ b/trunk/arch/powerpc/platforms/cell/spufs/inode.c @@ -186,13 +186,10 @@ static void spufs_prune_dir(struct dentry *dir) static int spufs_rmdir(struct inode *parent, struct dentry *dir) { /* remove all entries */ - int res; spufs_prune_dir(dir); d_drop(dir); - res = simple_rmdir(parent, dir); - /* We have to give up the mm_struct */ - spu_forget(SPUFS_I(dir->d_inode)->i_ctx); - return res; + + return simple_rmdir(parent, dir); } static int spufs_fill_dir(struct dentry *dir, @@ -248,6 +245,9 @@ static int spufs_dir_close(struct inode *inode, struct file *file) mutex_unlock(&parent->i_mutex); WARN_ON(ret); + /* We have to give up the mm_struct */ + spu_forget(ctx); + return dcache_dir_close(inode, file); } @@ -450,24 +450,28 @@ spufs_create_context(struct inode *inode, struct dentry *dentry, struct spu_context *neighbor; struct path path = {.mnt = mnt, .dentry = dentry}; + ret = -EPERM; if ((flags & SPU_CREATE_NOSCHED) && !capable(CAP_SYS_NICE)) - return -EPERM; + goto out_unlock; + ret = -EINVAL; if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE)) == SPU_CREATE_ISOLATE) - return -EINVAL; + goto out_unlock; + ret = -ENODEV; if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader) - return -ENODEV; + goto out_unlock; gang = NULL; neighbor = NULL; affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU); if (affinity) { gang = SPUFS_I(inode)->i_gang; + ret = -EINVAL; if (!gang) - return -EINVAL; + goto out_unlock; mutex_lock(&gang->aff_mutex); neighbor = spufs_assert_affinity(flags, gang, aff_filp); if (IS_ERR(neighbor)) { @@ -488,12 +492,22 @@ spufs_create_context(struct inode *inode, struct dentry *dentry, } ret = spufs_context_open(&path); - if (ret < 0) + if (ret < 0) { WARN_ON(spufs_rmdir(inode, dentry)); + if (affinity) + mutex_unlock(&gang->aff_mutex); + mutex_unlock(&inode->i_mutex); + spu_forget(SPUFS_I(dentry->d_inode)->i_ctx); + goto out; + } out_aff_unlock: if (affinity) mutex_unlock(&gang->aff_mutex); +out_unlock: + mutex_unlock(&inode->i_mutex); +out: + dput(dentry); return ret; } @@ -566,13 +580,18 @@ static int spufs_create_gang(struct inode *inode, int ret; ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO); - if (!ret) { - ret = spufs_gang_open(&path); - if (ret < 0) { - int err = simple_rmdir(inode, dentry); - WARN_ON(err); - } + if (ret) + goto out; + + ret = spufs_gang_open(&path); + if (ret < 0) { + int err = simple_rmdir(inode, dentry); + WARN_ON(err); } + +out: + mutex_unlock(&inode->i_mutex); + dput(dentry); return ret; } @@ -582,32 +601,40 @@ static struct file_system_type spufs_type; long spufs_create(struct path *path, struct dentry *dentry, unsigned int flags, umode_t mode, struct file *filp) { - struct inode *dir = path->dentry->d_inode; int ret; + ret = -EINVAL; /* check if we are on spufs */ if (path->dentry->d_sb->s_type != &spufs_type) - return -EINVAL; + goto out; /* don't accept undefined flags */ if (flags & (~SPU_CREATE_FLAG_ALL)) - return -EINVAL; + goto out; /* only threads can be underneath a gang */ - if (path->dentry != path->dentry->d_sb->s_root) - if ((flags & SPU_CREATE_GANG) || !SPUFS_I(dir)->i_gang) - return -EINVAL; + if (path->dentry != path->dentry->d_sb->s_root) { + if ((flags & SPU_CREATE_GANG) || + !SPUFS_I(path->dentry->d_inode)->i_gang) + goto out; + } mode &= ~current_umask(); if (flags & SPU_CREATE_GANG) - ret = spufs_create_gang(dir, dentry, path->mnt, mode); + ret = spufs_create_gang(path->dentry->d_inode, + dentry, path->mnt, mode); else - ret = spufs_create_context(dir, dentry, path->mnt, flags, mode, + ret = spufs_create_context(path->dentry->d_inode, + dentry, path->mnt, flags, mode, filp); if (ret >= 0) - fsnotify_mkdir(dir, dentry); + fsnotify_mkdir(path->dentry->d_inode, dentry); + return ret; +out: + mutex_unlock(&path->dentry->d_inode->i_mutex); + dput(dentry); return ret; } diff --git a/trunk/arch/powerpc/platforms/cell/spufs/syscalls.c b/trunk/arch/powerpc/platforms/cell/spufs/syscalls.c index 5b7d8ffbf890..5665dcc382c7 100644 --- a/trunk/arch/powerpc/platforms/cell/spufs/syscalls.c +++ b/trunk/arch/powerpc/platforms/cell/spufs/syscalls.c @@ -70,7 +70,7 @@ static long do_spu_create(const char __user *pathname, unsigned int flags, ret = PTR_ERR(dentry); if (!IS_ERR(dentry)) { ret = spufs_create(&path, dentry, flags, mode, neighbor); - done_path_create(&path, dentry); + path_put(&path); } return ret; diff --git a/trunk/arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h b/trunk/arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h index 2aa97ddb7b78..60c9c0bd5ba2 100644 --- a/trunk/arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h +++ b/trunk/arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h @@ -1,5 +1,5 @@ /* - * Copyright 2009-2010, 2012 Freescale Semiconductor, Inc + * Copyright 2009-2010 Freescale Semiconductor, Inc * * QorIQ based Cache Controller Memory Mapped Registers * @@ -91,7 +91,7 @@ struct mpc85xx_l2ctlr { struct sram_parameters { unsigned int sram_size; - phys_addr_t sram_offset; + uint64_t sram_offset; }; extern int instantiate_cache_sram(struct platform_device *dev, diff --git a/trunk/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c b/trunk/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c index 68ac3aacb191..cedabd0f4bfe 100644 --- a/trunk/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c +++ b/trunk/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2010, 2012 Freescale Semiconductor, Inc. + * Copyright 2009-2010 Freescale Semiconductor, Inc. * * QorIQ (P1/P2) L2 controller init for Cache-SRAM instantiation * @@ -31,21 +31,24 @@ static char *sram_size; static char *sram_offset; struct mpc85xx_l2ctlr __iomem *l2ctlr; -static int get_cache_sram_params(struct sram_parameters *sram_params) +static long get_cache_sram_size(void) { - unsigned long long addr; - unsigned int size; + unsigned long val; - if (!sram_size || (kstrtouint(sram_size, 0, &size) < 0)) + if (!sram_size || (strict_strtoul(sram_size, 0, &val) < 0)) return -EINVAL; - if (!sram_offset || (kstrtoull(sram_offset, 0, &addr) < 0)) - return -EINVAL; + return val; +} - sram_params->sram_offset = addr; - sram_params->sram_size = size; +static long get_cache_sram_offset(void) +{ + unsigned long val; - return 0; + if (!sram_offset || (strict_strtoul(sram_offset, 0, &val) < 0)) + return -EINVAL; + + return val; } static int __init get_size_from_cmdline(char *str) @@ -90,9 +93,17 @@ static int __devinit mpc85xx_l2ctlr_of_probe(struct platform_device *dev) } l2cache_size = *prop; - if (get_cache_sram_params(&sram_params)) { + sram_params.sram_size = get_cache_sram_size(); + if ((int)sram_params.sram_size <= 0) { + dev_err(&dev->dev, + "Entire L2 as cache, Aborting Cache-SRAM stuff\n"); + return -EINVAL; + } + + sram_params.sram_offset = get_cache_sram_offset(); + if ((int64_t)sram_params.sram_offset <= 0) { dev_err(&dev->dev, - "Entire L2 as cache, provide valid sram offset and size\n"); + "Entire L2 as cache, provide a valid sram offset\n"); return -EINVAL; } @@ -114,14 +125,14 @@ static int __devinit mpc85xx_l2ctlr_of_probe(struct platform_device *dev) * Write bits[0-17] to srbar0 */ out_be32(&l2ctlr->srbar0, - lower_32_bits(sram_params.sram_offset) & L2SRAM_BAR_MSK_LO18); + sram_params.sram_offset & L2SRAM_BAR_MSK_LO18); /* * Write bits[18-21] to srbare0 */ #ifdef CONFIG_PHYS_64BIT out_be32(&l2ctlr->srbarea0, - upper_32_bits(sram_params.sram_offset) & L2SRAM_BARE_MSK_HI4); + (sram_params.sram_offset >> 32) & L2SRAM_BARE_MSK_HI4); #endif clrsetbits_be32(&l2ctlr->ctl, L2CR_L2E, L2CR_L2FI); diff --git a/trunk/arch/powerpc/sysdev/xics/icp-hv.c b/trunk/arch/powerpc/sysdev/xics/icp-hv.c index 14469cf9df68..253dce98c16e 100644 --- a/trunk/arch/powerpc/sysdev/xics/icp-hv.c +++ b/trunk/arch/powerpc/sysdev/xics/icp-hv.c @@ -111,7 +111,7 @@ static unsigned int icp_hv_get_irq(void) if (vec == XICS_IRQ_SPURIOUS) return NO_IRQ; - irq = irq_find_mapping(xics_host, vec); + irq = irq_radix_revmap_lookup(xics_host, vec); if (likely(irq != NO_IRQ)) { xics_push_cppr(vec); return irq; diff --git a/trunk/arch/powerpc/sysdev/xics/icp-native.c b/trunk/arch/powerpc/sysdev/xics/icp-native.c index 48861d3fcd07..4c79b6fbee1c 100644 --- a/trunk/arch/powerpc/sysdev/xics/icp-native.c +++ b/trunk/arch/powerpc/sysdev/xics/icp-native.c @@ -119,7 +119,7 @@ static unsigned int icp_native_get_irq(void) if (vec == XICS_IRQ_SPURIOUS) return NO_IRQ; - irq = irq_find_mapping(xics_host, vec); + irq = irq_radix_revmap_lookup(xics_host, vec); if (likely(irq != NO_IRQ)) { xics_push_cppr(vec); return irq; diff --git a/trunk/arch/powerpc/sysdev/xics/xics-common.c b/trunk/arch/powerpc/sysdev/xics/xics-common.c index 9049d9f44485..cd1d18db92c6 100644 --- a/trunk/arch/powerpc/sysdev/xics/xics-common.c +++ b/trunk/arch/powerpc/sysdev/xics/xics-common.c @@ -329,6 +329,9 @@ static int xics_host_map(struct irq_domain *h, unsigned int virq, pr_devel("xics: map virq %d, hwirq 0x%lx\n", virq, hw); + /* Insert the interrupt mapping into the radix tree for fast lookup */ + irq_radix_revmap_insert(xics_host, virq, hw); + /* They aren't all level sensitive but we just don't really know */ irq_set_status_flags(virq, IRQ_LEVEL); diff --git a/trunk/arch/s390/Kconfig b/trunk/arch/s390/Kconfig index 76de6b68487c..296cd32466df 100644 --- a/trunk/arch/s390/Kconfig +++ b/trunk/arch/s390/Kconfig @@ -90,7 +90,6 @@ config S390 select HAVE_MEMBLOCK_NODE_MAP select HAVE_CMPXCHG_LOCAL select ARCH_DISCARD_MEMBLOCK - select BUILDTIME_EXTABLE_SORT select ARCH_INLINE_SPIN_TRYLOCK select ARCH_INLINE_SPIN_TRYLOCK_BH select ARCH_INLINE_SPIN_LOCK diff --git a/trunk/arch/s390/defconfig b/trunk/arch/s390/defconfig index f39cd710980b..37d2bf267964 100644 --- a/trunk/arch/s390/defconfig +++ b/trunk/arch/s390/defconfig @@ -7,16 +7,13 @@ CONFIG_TASK_DELAY_ACCT=y CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y CONFIG_AUDIT=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_RCU_FAST_NO_HZ=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEM_RES_CTLR=y CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y @@ -38,6 +35,8 @@ CONFIG_MODVERSIONS=y CONFIG_PARTITION_ADVANCED=y CONFIG_IBM_PARTITION=y CONFIG_DEFAULT_DEADLINE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT=y CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTREMOVE=y diff --git a/trunk/arch/s390/include/asm/mmu_context.h b/trunk/arch/s390/include/asm/mmu_context.h index b749c5733657..5c63615f1349 100644 --- a/trunk/arch/s390/include/asm/mmu_context.h +++ b/trunk/arch/s390/include/asm/mmu_context.h @@ -11,6 +11,7 @@ #include #include #include +#include static inline int init_new_context(struct task_struct *tsk, struct mm_struct *mm) @@ -57,7 +58,7 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk) pgd_t *pgd = mm->pgd; S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd); - if (addressing_mode != HOME_SPACE_MODE) { + if (user_mode != HOME_SPACE_MODE) { /* Load primary space page table origin. */ asm volatile(LCTL_OPCODE" 1,1,%0\n" : : "m" (S390_lowcore.user_asce) ); @@ -90,17 +91,4 @@ static inline void activate_mm(struct mm_struct *prev, switch_mm(prev, next, current); } -static inline void arch_dup_mmap(struct mm_struct *oldmm, - struct mm_struct *mm) -{ -#ifdef CONFIG_64BIT - if (oldmm->context.asce_limit < mm->context.asce_limit) - crst_table_downgrade(mm, oldmm->context.asce_limit); -#endif -} - -static inline void arch_exit_mmap(struct mm_struct *mm) -{ -} - #endif /* __S390_MMU_CONTEXT_H */ diff --git a/trunk/arch/s390/include/asm/processor.h b/trunk/arch/s390/include/asm/processor.h index 11e4e3236937..c40fa91e38a8 100644 --- a/trunk/arch/s390/include/asm/processor.h +++ b/trunk/arch/s390/include/asm/processor.h @@ -120,9 +120,7 @@ struct stack_frame { regs->psw.mask = psw_user_bits | PSW_MASK_BA; \ regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ regs->gprs[15] = new_stackp; \ - __tlb_flush_mm(current->mm); \ crst_table_downgrade(current->mm, 1UL << 31); \ - update_mm(current->mm, current); \ } while (0) /* Forward declaration, a strange C thing */ diff --git a/trunk/arch/s390/include/asm/setup.h b/trunk/arch/s390/include/asm/setup.h index e6859d16ee2d..57e80534375a 100644 --- a/trunk/arch/s390/include/asm/setup.h +++ b/trunk/arch/s390/include/asm/setup.h @@ -60,7 +60,7 @@ void create_mem_hole(struct mem_chunk memory_chunk[], unsigned long addr, #define SECONDARY_SPACE_MODE 2 #define HOME_SPACE_MODE 3 -extern unsigned int addressing_mode; +extern unsigned int user_mode; /* * Machine features detected in head.S diff --git a/trunk/arch/s390/kernel/debug.c b/trunk/arch/s390/kernel/debug.c index ba500d8dc392..21be961e8a43 100644 --- a/trunk/arch/s390/kernel/debug.c +++ b/trunk/arch/s390/kernel/debug.c @@ -110,7 +110,6 @@ struct debug_view debug_raw_view = { NULL, NULL }; -EXPORT_SYMBOL(debug_raw_view); struct debug_view debug_hex_ascii_view = { "hex_ascii", @@ -120,7 +119,6 @@ struct debug_view debug_hex_ascii_view = { NULL, NULL }; -EXPORT_SYMBOL(debug_hex_ascii_view); static struct debug_view debug_level_view = { "level", @@ -157,7 +155,6 @@ struct debug_view debug_sprintf_view = { NULL, NULL }; -EXPORT_SYMBOL(debug_sprintf_view); /* used by dump analysis tools to determine version of debug feature */ static unsigned int __used debug_feature_version = __DEBUG_FEATURE_VERSION; @@ -733,7 +730,6 @@ debug_info_t *debug_register(const char *name, int pages_per_area, return debug_register_mode(name, pages_per_area, nr_areas, buf_size, S_IRUSR | S_IWUSR, 0, 0); } -EXPORT_SYMBOL(debug_register); /* * debug_unregister: @@ -752,7 +748,6 @@ debug_unregister(debug_info_t * id) out: return; } -EXPORT_SYMBOL(debug_unregister); /* * debug_set_size: @@ -815,7 +810,7 @@ debug_set_level(debug_info_t* id, int new_level) } spin_unlock_irqrestore(&id->lock,flags); } -EXPORT_SYMBOL(debug_set_level); + /* * proceed_active_entry: @@ -935,7 +930,7 @@ debug_stop_all(void) if (debug_stoppable) debug_active = 0; } -EXPORT_SYMBOL(debug_stop_all); + void debug_set_critical(void) { @@ -968,7 +963,6 @@ debug_event_common(debug_info_t * id, int level, const void *buf, int len) return active; } -EXPORT_SYMBOL(debug_event_common); /* * debug_exception_common: @@ -996,7 +990,6 @@ debug_entry_t return active; } -EXPORT_SYMBOL(debug_exception_common); /* * counts arguments in format string for sprintf view @@ -1050,7 +1043,6 @@ debug_sprintf_event(debug_info_t* id, int level,char *string,...) return active; } -EXPORT_SYMBOL(debug_sprintf_event); /* * debug_sprintf_exception: @@ -1089,7 +1081,25 @@ debug_sprintf_exception(debug_info_t* id, int level,char *string,...) return active; } -EXPORT_SYMBOL(debug_sprintf_exception); + +/* + * debug_init: + * - is called exactly once to initialize the debug feature + */ + +static int +__init debug_init(void) +{ + int rc = 0; + + s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table); + mutex_lock(&debug_mutex); + debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT,NULL); + initialized = 1; + mutex_unlock(&debug_mutex); + + return rc; +} /* * debug_register_view: @@ -1137,7 +1147,6 @@ debug_register_view(debug_info_t * id, struct debug_view *view) out: return rc; } -EXPORT_SYMBOL(debug_register_view); /* * debug_unregister_view: @@ -1167,7 +1176,6 @@ debug_unregister_view(debug_info_t * id, struct debug_view *view) out: return rc; } -EXPORT_SYMBOL(debug_unregister_view); static inline char * debug_get_user_string(const char __user *user_buf, size_t user_len) @@ -1477,7 +1485,6 @@ debug_dflt_header_fn(debug_info_t * id, struct debug_view *view, except_str, entry->id.fields.cpuid, (void *) caller); return rc; } -EXPORT_SYMBOL(debug_dflt_header_fn); /* * prints debug data sprintf-formated: @@ -1526,16 +1533,33 @@ debug_sprintf_format_fn(debug_info_t * id, struct debug_view *view, } /* - * debug_init: - * - is called exactly once to initialize the debug feature + * clean up module */ -static int __init debug_init(void) +static void __exit debug_exit(void) { - s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table); - mutex_lock(&debug_mutex); - debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT, NULL); - initialized = 1; - mutex_unlock(&debug_mutex); - return 0; + debugfs_remove(debug_debugfs_root_entry); + unregister_sysctl_table(s390dbf_sysctl_header); + return; } + +/* + * module definitions + */ postcore_initcall(debug_init); +module_exit(debug_exit); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(debug_register); +EXPORT_SYMBOL(debug_unregister); +EXPORT_SYMBOL(debug_set_level); +EXPORT_SYMBOL(debug_stop_all); +EXPORT_SYMBOL(debug_register_view); +EXPORT_SYMBOL(debug_unregister_view); +EXPORT_SYMBOL(debug_event_common); +EXPORT_SYMBOL(debug_exception_common); +EXPORT_SYMBOL(debug_hex_ascii_view); +EXPORT_SYMBOL(debug_raw_view); +EXPORT_SYMBOL(debug_dflt_header_fn); +EXPORT_SYMBOL(debug_sprintf_view); +EXPORT_SYMBOL(debug_sprintf_exception); +EXPORT_SYMBOL(debug_sprintf_event); diff --git a/trunk/arch/s390/kernel/dis.c b/trunk/arch/s390/kernel/dis.c index 619c5d350726..1f6b428e2762 100644 --- a/trunk/arch/s390/kernel/dis.c +++ b/trunk/arch/s390/kernel/dis.c @@ -1531,7 +1531,7 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr) void show_code(struct pt_regs *regs) { - char *mode = user_mode(regs) ? "User" : "Krnl"; + char *mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl"; unsigned char code[64]; char buffer[64], *ptr; mm_segment_t old_fs; @@ -1540,7 +1540,7 @@ void show_code(struct pt_regs *regs) /* Get a snapshot of the 64 bytes surrounding the fault address. */ old_fs = get_fs(); - set_fs(user_mode(regs) ? USER_DS : KERNEL_DS); + set_fs((regs->psw.mask & PSW_MASK_PSTATE) ? USER_DS : KERNEL_DS); for (start = 32; start && regs->psw.addr >= 34 - start; start -= 2) { addr = regs->psw.addr - 34 + start; if (__copy_from_user(code + start - 2, diff --git a/trunk/arch/s390/kernel/early.c b/trunk/arch/s390/kernel/early.c index 83c3271c442b..bc95a8ebd9cc 100644 --- a/trunk/arch/s390/kernel/early.c +++ b/trunk/arch/s390/kernel/early.c @@ -455,6 +455,7 @@ void __init startup_init(void) init_kernel_storage_key(); lockdep_init(); lockdep_off(); + sort_main_extable(); setup_lowcore_early(); setup_facility_list(); detect_machine_type(); diff --git a/trunk/arch/s390/kernel/ipl.c b/trunk/arch/s390/kernel/ipl.c index 6ffcd3203215..e64d141555ce 100644 --- a/trunk/arch/s390/kernel/ipl.c +++ b/trunk/arch/s390/kernel/ipl.c @@ -1583,7 +1583,7 @@ static struct kset *vmcmd_kset; static void vmcmd_run(struct shutdown_trigger *trigger) { - char *cmd; + char *cmd, *next_cmd; if (strcmp(trigger->name, ON_REIPL_STR) == 0) cmd = vmcmd_on_reboot; @@ -1600,7 +1600,15 @@ static void vmcmd_run(struct shutdown_trigger *trigger) if (strlen(cmd) == 0) return; - __cpcmd(cmd, NULL, 0, NULL); + do { + next_cmd = strchr(cmd, '\n'); + if (next_cmd) { + next_cmd[0] = 0; + next_cmd += 1; + } + __cpcmd(cmd, NULL, 0, NULL); + cmd = next_cmd; + } while (cmd != NULL); } static int vmcmd_init(void) diff --git a/trunk/arch/s390/kernel/setup.c b/trunk/arch/s390/kernel/setup.c index f86c81e13c37..743c0f32fe3b 100644 --- a/trunk/arch/s390/kernel/setup.c +++ b/trunk/arch/s390/kernel/setup.c @@ -302,8 +302,8 @@ static int __init parse_vmalloc(char *arg) } early_param("vmalloc", parse_vmalloc); -unsigned int addressing_mode = HOME_SPACE_MODE; -EXPORT_SYMBOL_GPL(addressing_mode); +unsigned int user_mode = HOME_SPACE_MODE; +EXPORT_SYMBOL_GPL(user_mode); static int set_amode_primary(void) { @@ -328,7 +328,7 @@ static int set_amode_primary(void) */ static int __init early_parse_switch_amode(char *p) { - addressing_mode = PRIMARY_SPACE_MODE; + user_mode = PRIMARY_SPACE_MODE; return 0; } early_param("switch_amode", early_parse_switch_amode); @@ -336,9 +336,9 @@ early_param("switch_amode", early_parse_switch_amode); static int __init early_parse_user_mode(char *p) { if (p && strcmp(p, "primary") == 0) - addressing_mode = PRIMARY_SPACE_MODE; + user_mode = PRIMARY_SPACE_MODE; else if (!p || strcmp(p, "home") == 0) - addressing_mode = HOME_SPACE_MODE; + user_mode = HOME_SPACE_MODE; else return 1; return 0; @@ -347,7 +347,7 @@ early_param("user_mode", early_parse_user_mode); static void setup_addressing_mode(void) { - if (addressing_mode == PRIMARY_SPACE_MODE) { + if (user_mode == PRIMARY_SPACE_MODE) { if (set_amode_primary()) pr_info("Address spaces switched, " "mvcos available\n"); diff --git a/trunk/arch/s390/kernel/traps.c b/trunk/arch/s390/kernel/traps.c index 01775c04a90e..af2421a0f315 100644 --- a/trunk/arch/s390/kernel/traps.c +++ b/trunk/arch/s390/kernel/traps.c @@ -185,7 +185,7 @@ void show_registers(struct pt_regs *regs) { char *mode; - mode = user_mode(regs) ? "User" : "Krnl"; + mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl"; printk("%s PSW : %p %p", mode, (void *) regs->psw.mask, (void *) regs->psw.addr); @@ -225,7 +225,7 @@ void show_regs(struct pt_regs *regs) (void *) current->thread.ksp); show_registers(regs); /* Show stack backtrace if pt_regs is from kernel mode */ - if (!user_mode(regs)) + if (!(regs->psw.mask & PSW_MASK_PSTATE)) show_trace(NULL, (unsigned long *) regs->gprs[15]); show_last_breaking_event(regs); } @@ -300,7 +300,7 @@ static void __kprobes do_trap(struct pt_regs *regs, regs->int_code, si_signo) == NOTIFY_STOP) return; - if (user_mode(regs)) { + if (regs->psw.mask & PSW_MASK_PSTATE) { info.si_signo = si_signo; info.si_errno = 0; info.si_code = si_code; @@ -341,7 +341,7 @@ void __kprobes do_per_trap(struct pt_regs *regs) static void default_trap_handler(struct pt_regs *regs) { - if (user_mode(regs)) { + if (regs->psw.mask & PSW_MASK_PSTATE) { report_user_fault(regs, SIGSEGV); do_exit(SIGSEGV); } else @@ -410,7 +410,7 @@ static void __kprobes illegal_op(struct pt_regs *regs) location = get_psw_address(regs); - if (user_mode(regs)) { + if (regs->psw.mask & PSW_MASK_PSTATE) { if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) return; if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) { @@ -478,7 +478,7 @@ void specification_exception(struct pt_regs *regs) location = (__u16 __user *) get_psw_address(regs); - if (user_mode(regs)) { + if (regs->psw.mask & PSW_MASK_PSTATE) { get_user(*((__u16 *) opcode), location); switch (opcode[0]) { case 0x28: /* LDR Rx,Ry */ @@ -531,7 +531,7 @@ static void data_exception(struct pt_regs *regs) asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); #ifdef CONFIG_MATHEMU - else if (user_mode(regs)) { + else if (regs->psw.mask & PSW_MASK_PSTATE) { __u8 opcode[6]; get_user(*((__u16 *) opcode), location); switch (opcode[0]) { @@ -598,7 +598,7 @@ static void data_exception(struct pt_regs *regs) static void space_switch_exception(struct pt_regs *regs) { /* Set user psw back to home space mode. */ - if (user_mode(regs)) + if (regs->psw.mask & PSW_MASK_PSTATE) regs->psw.mask |= PSW_ASC_HOME; /* Send SIGILL. */ do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); diff --git a/trunk/arch/s390/kernel/vdso.c b/trunk/arch/s390/kernel/vdso.c index 9a19ca367c17..ea5590fdca3b 100644 --- a/trunk/arch/s390/kernel/vdso.c +++ b/trunk/arch/s390/kernel/vdso.c @@ -84,8 +84,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data; */ static void vdso_init_data(struct vdso_data *vd) { - vd->ectg_available = - addressing_mode != HOME_SPACE_MODE && test_facility(31); + vd->ectg_available = user_mode != HOME_SPACE_MODE && test_facility(31); } #ifdef CONFIG_64BIT @@ -102,7 +101,7 @@ int vdso_alloc_per_cpu(struct _lowcore *lowcore) lowcore->vdso_per_cpu_data = __LC_PASTE; - if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled) + if (user_mode == HOME_SPACE_MODE || !vdso_enabled) return 0; segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER); @@ -147,7 +146,7 @@ void vdso_free_per_cpu(struct _lowcore *lowcore) unsigned long segment_table, page_table, page_frame; u32 *psal, *aste; - if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled) + if (user_mode == HOME_SPACE_MODE || !vdso_enabled) return; psal = (u32 *)(addr_t) lowcore->paste[4]; @@ -165,7 +164,7 @@ static void vdso_init_cr5(void) { unsigned long cr5; - if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled) + if (user_mode == HOME_SPACE_MODE || !vdso_enabled) return; cr5 = offsetof(struct _lowcore, paste); __ctl_load(cr5, 5, 5); diff --git a/trunk/arch/s390/kernel/vmlinux.lds.S b/trunk/arch/s390/kernel/vmlinux.lds.S index de8fa9bbd35e..21109c63eb12 100644 --- a/trunk/arch/s390/kernel/vmlinux.lds.S +++ b/trunk/arch/s390/kernel/vmlinux.lds.S @@ -45,7 +45,7 @@ SECTIONS .dummy : { *(.dummy) } :data - RO_DATA_SECTION(PAGE_SIZE) + RODATA #ifdef CONFIG_SHARED_KERNEL . = ALIGN(0x100000); /* VM shared segments are 1MB aligned */ diff --git a/trunk/arch/s390/mm/fault.c b/trunk/arch/s390/mm/fault.c index 6c013f544146..6a12d1bb6e09 100644 --- a/trunk/arch/s390/mm/fault.c +++ b/trunk/arch/s390/mm/fault.c @@ -49,7 +49,6 @@ #define VM_FAULT_BADCONTEXT 0x010000 #define VM_FAULT_BADMAP 0x020000 #define VM_FAULT_BADACCESS 0x040000 -#define VM_FAULT_SIGNAL 0x080000 static unsigned long store_indication; @@ -111,7 +110,7 @@ static inline int user_space_fault(unsigned long trans_exc_code) if (trans_exc_code == 2) /* Access via secondary space, set_fs setting decides */ return current->thread.mm_segment.ar4; - if (addressing_mode == HOME_SPACE_MODE) + if (user_mode == HOME_SPACE_MODE) /* User space if the access has been done via home space. */ return trans_exc_code == 3; /* @@ -220,7 +219,7 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault) case VM_FAULT_BADACCESS: case VM_FAULT_BADMAP: /* Bad memory access. Check if it is kernel or user space. */ - if (user_mode(regs)) { + if (regs->psw.mask & PSW_MASK_PSTATE) { /* User mode accesses just cause a SIGSEGV */ si_code = (fault == VM_FAULT_BADMAP) ? SEGV_MAPERR : SEGV_ACCERR; @@ -230,19 +229,15 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault) case VM_FAULT_BADCONTEXT: do_no_context(regs); break; - case VM_FAULT_SIGNAL: - if (!user_mode(regs)) - do_no_context(regs); - break; default: /* fault & VM_FAULT_ERROR */ if (fault & VM_FAULT_OOM) { - if (!user_mode(regs)) + if (!(regs->psw.mask & PSW_MASK_PSTATE)) do_no_context(regs); else pagefault_out_of_memory(); } else if (fault & VM_FAULT_SIGBUS) { /* Kernel mode? Handle exceptions or die */ - if (!user_mode(regs)) + if (!(regs->psw.mask & PSW_MASK_PSTATE)) do_no_context(regs); else do_sigbus(regs); @@ -291,7 +286,7 @@ static inline int do_exception(struct pt_regs *regs, int access) address = trans_exc_code & __FAIL_ADDR_MASK; perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); - flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; + flags = FAULT_FLAG_ALLOW_RETRY; if (access == VM_WRITE || (trans_exc_code & store_indication) == 0x400) flags |= FAULT_FLAG_WRITE; down_read(&mm->mmap_sem); @@ -340,11 +335,6 @@ static inline int do_exception(struct pt_regs *regs, int access) * the fault. */ fault = handle_mm_fault(mm, vma, address, flags); - /* No reason to continue if interrupted by SIGKILL. */ - if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) { - fault = VM_FAULT_SIGNAL; - goto out; - } if (unlikely(fault & VM_FAULT_ERROR)) goto out_up; @@ -436,7 +426,7 @@ void __kprobes do_asce_exception(struct pt_regs *regs) } /* User mode accesses just cause a SIGSEGV */ - if (user_mode(regs)) { + if (regs->psw.mask & PSW_MASK_PSTATE) { do_sigsegv(regs, SEGV_MAPERR); return; } @@ -451,7 +441,6 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) struct pt_regs regs; int access, fault; - /* Emulate a uaccess fault from kernel mode. */ regs.psw.mask = psw_kernel_bits | PSW_MASK_DAT | PSW_MASK_MCHECK; if (!irqs_disabled()) regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT; @@ -461,12 +450,12 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) regs.int_parm_long = (uaddr & PAGE_MASK) | 2; access = write ? VM_WRITE : VM_READ; fault = do_exception(®s, access); - /* - * Since the fault happened in kernel mode while performing a uaccess - * all we need to do now is emulating a fixup in case "fault" is not - * zero. - * For the calling uaccess functions this results always in -EFAULT. - */ + if (unlikely(fault)) { + if (fault & VM_FAULT_OOM) + return -EFAULT; + else if (fault & VM_FAULT_SIGBUS) + do_sigbus(®s); + } return fault ? -EFAULT : 0; } diff --git a/trunk/arch/s390/mm/mmap.c b/trunk/arch/s390/mm/mmap.c index c59a5efa58b1..573384256c5c 100644 --- a/trunk/arch/s390/mm/mmap.c +++ b/trunk/arch/s390/mm/mmap.c @@ -103,15 +103,9 @@ void arch_pick_mmap_layout(struct mm_struct *mm) int s390_mmap_check(unsigned long addr, unsigned long len) { - int rc; - if (!is_compat_task() && - len >= TASK_SIZE && TASK_SIZE < (1UL << 53)) { - rc = crst_table_upgrade(current->mm, 1UL << 53); - if (rc) - return rc; - update_mm(current->mm, current); - } + len >= TASK_SIZE && TASK_SIZE < (1UL << 53)) + return crst_table_upgrade(current->mm, 1UL << 53); return 0; } @@ -131,7 +125,6 @@ s390_get_unmapped_area(struct file *filp, unsigned long addr, rc = crst_table_upgrade(mm, 1UL << 53); if (rc) return (unsigned long) rc; - update_mm(mm, current); area = arch_get_unmapped_area(filp, addr, len, pgoff, flags); } return area; @@ -154,7 +147,6 @@ s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr, rc = crst_table_upgrade(mm, 1UL << 53); if (rc) return (unsigned long) rc; - update_mm(mm, current); area = arch_get_unmapped_area_topdown(filp, addr, len, pgoff, flags); } diff --git a/trunk/arch/s390/mm/pgtable.c b/trunk/arch/s390/mm/pgtable.c index 18df31d1f2c9..1cab221077cc 100644 --- a/trunk/arch/s390/mm/pgtable.c +++ b/trunk/arch/s390/mm/pgtable.c @@ -85,6 +85,7 @@ int crst_table_upgrade(struct mm_struct *mm, unsigned long limit) crst_table_free(mm, table); if (mm->context.asce_limit < limit) goto repeat; + update_mm(mm, current); return 0; } @@ -92,6 +93,9 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit) { pgd_t *pgd; + if (mm->context.asce_limit <= limit) + return; + __tlb_flush_mm(mm); while (mm->context.asce_limit > limit) { pgd = mm->pgd; switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) { @@ -114,6 +118,7 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit) mm->task_size = mm->context.asce_limit; crst_table_free(mm, (unsigned long *) pgd); } + update_mm(mm, current); } #endif @@ -796,7 +801,7 @@ int s390_enable_sie(void) struct mm_struct *mm, *old_mm; /* Do we have switched amode? If no, we cannot do sie */ - if (addressing_mode == HOME_SPACE_MODE) + if (user_mode == HOME_SPACE_MODE) return -EINVAL; /* Do we have pgstes? if yes, we are done */ diff --git a/trunk/arch/s390/oprofile/backtrace.c b/trunk/arch/s390/oprofile/backtrace.c index 8a6811b2cdb9..c82f62fb9c28 100644 --- a/trunk/arch/s390/oprofile/backtrace.c +++ b/trunk/arch/s390/oprofile/backtrace.c @@ -58,7 +58,7 @@ void s390_backtrace(struct pt_regs * const regs, unsigned int depth) unsigned long head; struct stack_frame* head_sf; - if (user_mode(regs)) + if (user_mode (regs)) return; head = regs->gprs[15]; diff --git a/trunk/arch/sh/configs/apsh4ad0a_defconfig b/trunk/arch/sh/configs/apsh4ad0a_defconfig index 95ae23fcfdd6..e7583484cc07 100644 --- a/trunk/arch/sh/configs/apsh4ad0a_defconfig +++ b/trunk/arch/sh/configs/apsh4ad0a_defconfig @@ -11,7 +11,7 @@ CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEM_RES_CTLR=y CONFIG_BLK_CGROUP=y CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y diff --git a/trunk/arch/sh/configs/sdk7786_defconfig b/trunk/arch/sh/configs/sdk7786_defconfig index 76a76a295d74..8a7dd7b59c5c 100644 --- a/trunk/arch/sh/configs/sdk7786_defconfig +++ b/trunk/arch/sh/configs/sdk7786_defconfig @@ -18,8 +18,8 @@ CONFIG_CPUSETS=y # CONFIG_PROC_PID_CPUSET is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y -CONFIG_CGROUP_MEMCG_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/trunk/arch/sh/configs/se7206_defconfig b/trunk/arch/sh/configs/se7206_defconfig index 6bc30ab9fd18..72c3fad7383f 100644 --- a/trunk/arch/sh/configs/se7206_defconfig +++ b/trunk/arch/sh/configs/se7206_defconfig @@ -11,7 +11,7 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEM_RES_CTLR=y CONFIG_RELAY=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y diff --git a/trunk/arch/sh/configs/shx3_defconfig b/trunk/arch/sh/configs/shx3_defconfig index cd6c519f8fad..6bb413036892 100644 --- a/trunk/arch/sh/configs/shx3_defconfig +++ b/trunk/arch/sh/configs/shx3_defconfig @@ -13,7 +13,7 @@ CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEM_RES_CTLR=y CONFIG_RELAY=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y diff --git a/trunk/arch/sh/configs/urquell_defconfig b/trunk/arch/sh/configs/urquell_defconfig index d7f89be9f474..8bfa4d056d7a 100644 --- a/trunk/arch/sh/configs/urquell_defconfig +++ b/trunk/arch/sh/configs/urquell_defconfig @@ -15,8 +15,8 @@ CONFIG_CPUSETS=y # CONFIG_PROC_PID_CPUSET is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y -CONFIG_CGROUP_MEMCG_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_DEV_INITRD=y diff --git a/trunk/arch/sparc/kernel/ldc.c b/trunk/arch/sparc/kernel/ldc.c index 81d92fc9983b..435e406fdec3 100644 --- a/trunk/arch/sparc/kernel/ldc.c +++ b/trunk/arch/sparc/kernel/ldc.c @@ -1250,12 +1250,14 @@ int ldc_bind(struct ldc_channel *lp, const char *name) snprintf(lp->rx_irq_name, LDC_IRQ_NAME_MAX, "%s RX", name); snprintf(lp->tx_irq_name, LDC_IRQ_NAME_MAX, "%s TX", name); - err = request_irq(lp->cfg.rx_irq, ldc_rx, IRQF_DISABLED, + err = request_irq(lp->cfg.rx_irq, ldc_rx, + IRQF_SAMPLE_RANDOM | IRQF_DISABLED, lp->rx_irq_name, lp); if (err) return err; - err = request_irq(lp->cfg.tx_irq, ldc_tx, IRQF_DISABLED, + err = request_irq(lp->cfg.tx_irq, ldc_tx, + IRQF_SAMPLE_RANDOM | IRQF_DISABLED, lp->tx_irq_name, lp); if (err) { free_irq(lp->cfg.rx_irq, lp); diff --git a/trunk/arch/tile/configs/tilegx_defconfig b/trunk/arch/tile/configs/tilegx_defconfig index 0270620a1692..b8d99aca5431 100644 --- a/trunk/arch/tile/configs/tilegx_defconfig +++ b/trunk/arch/tile/configs/tilegx_defconfig @@ -18,8 +18,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y -CONFIG_CGROUP_MEMCG_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/trunk/arch/tile/configs/tilepro_defconfig b/trunk/arch/tile/configs/tilepro_defconfig index c11de27a9bcb..2b1fd31894f1 100644 --- a/trunk/arch/tile/configs/tilepro_defconfig +++ b/trunk/arch/tile/configs/tilepro_defconfig @@ -17,8 +17,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y -CONFIG_CGROUP_MEMCG_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/trunk/arch/um/defconfig b/trunk/arch/um/defconfig index 08107a795062..7823ab12e6a4 100644 --- a/trunk/arch/um/defconfig +++ b/trunk/arch/um/defconfig @@ -155,15 +155,15 @@ CONFIG_CPUSETS=y CONFIG_PROC_PID_CPUSET=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEMCG=y -CONFIG_CGROUP_MEMCG_SWAP=y -# CONFIG_CGROUP_MEMCG_SWAP_ENABLED is not set -# CONFIG_CGROUP_MEMCG_KMEM is not set +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +# CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED is not set +# CONFIG_CGROUP_MEM_RES_CTLR_KMEM is not set CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y # CONFIG_CFS_BANDWIDTH is not set # CONFIG_RT_GROUP_SCHED is not set -CONFIG_BLK_CGROUP=y +CONFIG_BLK_CGROUP=m # CONFIG_DEBUG_BLK_CGROUP is not set # CONFIG_CHECKPOINT_RESTORE is not set CONFIG_NAMESPACES=y diff --git a/trunk/arch/um/drivers/chan_kern.c b/trunk/arch/um/drivers/chan_kern.c index 87eebfe03c61..45e248c2f43c 100644 --- a/trunk/arch/um/drivers/chan_kern.c +++ b/trunk/arch/um/drivers/chan_kern.c @@ -150,11 +150,9 @@ void chan_enable_winch(struct chan *chan, struct tty_struct *tty) static void line_timer_cb(struct work_struct *work) { struct line *line = container_of(work, struct line, task.work); - struct tty_struct *tty = tty_port_tty_get(&line->port); if (!line->throttled) - chan_interrupt(line, tty, line->driver->read_irq); - tty_kref_put(tty); + chan_interrupt(line, line->tty, line->driver->read_irq); } int enable_chan(struct line *line) diff --git a/trunk/arch/um/drivers/line.c b/trunk/arch/um/drivers/line.c index bbaf2c59830a..acfd0e0fd0c9 100644 --- a/trunk/arch/um/drivers/line.c +++ b/trunk/arch/um/drivers/line.c @@ -19,11 +19,9 @@ static irqreturn_t line_interrupt(int irq, void *data) { struct chan *chan = data; struct line *line = chan->line; - struct tty_struct *tty = tty_port_tty_get(&line->port); if (line) - chan_interrupt(line, tty, irq); - tty_kref_put(tty); + chan_interrupt(line, line->tty, irq); return IRQ_HANDLED; } @@ -221,6 +219,92 @@ void line_set_termios(struct tty_struct *tty, struct ktermios * old) /* nothing */ } +static const struct { + int cmd; + char *level; + char *name; +} tty_ioctls[] = { + /* don't print these, they flood the log ... */ + { TCGETS, NULL, "TCGETS" }, + { TCSETS, NULL, "TCSETS" }, + { TCSETSW, NULL, "TCSETSW" }, + { TCFLSH, NULL, "TCFLSH" }, + { TCSBRK, NULL, "TCSBRK" }, + + /* general tty stuff */ + { TCSETSF, KERN_DEBUG, "TCSETSF" }, + { TCGETA, KERN_DEBUG, "TCGETA" }, + { TIOCMGET, KERN_DEBUG, "TIOCMGET" }, + { TCSBRKP, KERN_DEBUG, "TCSBRKP" }, + { TIOCMSET, KERN_DEBUG, "TIOCMSET" }, + + /* linux-specific ones */ + { TIOCLINUX, KERN_INFO, "TIOCLINUX" }, + { KDGKBMODE, KERN_INFO, "KDGKBMODE" }, + { KDGKBTYPE, KERN_INFO, "KDGKBTYPE" }, + { KDSIGACCEPT, KERN_INFO, "KDSIGACCEPT" }, +}; + +int line_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) +{ + int ret; + int i; + + ret = 0; + switch(cmd) { +#ifdef TIOCGETP + case TIOCGETP: + case TIOCSETP: + case TIOCSETN: +#endif +#ifdef TIOCGETC + case TIOCGETC: + case TIOCSETC: +#endif +#ifdef TIOCGLTC + case TIOCGLTC: + case TIOCSLTC: +#endif + /* Note: these are out of date as we now have TCGETS2 etc but this + whole lot should probably go away */ + case TCGETS: + case TCSETSF: + case TCSETSW: + case TCSETS: + case TCGETA: + case TCSETAF: + case TCSETAW: + case TCSETA: + case TCXONC: + case TCFLSH: + case TIOCOUTQ: + case TIOCINQ: + case TIOCGLCKTRMIOS: + case TIOCSLCKTRMIOS: + case TIOCPKT: + case TIOCGSOFTCAR: + case TIOCSSOFTCAR: + return -ENOIOCTLCMD; +#if 0 + case TCwhatever: + /* do something */ + break; +#endif + default: + for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++) + if (cmd == tty_ioctls[i].cmd) + break; + if (i == ARRAY_SIZE(tty_ioctls)) { + printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n", + __func__, tty->name, cmd); + } + ret = -ENOIOCTLCMD; + break; + } + return ret; +} + void line_throttle(struct tty_struct *tty) { struct line *line = tty->driver_data; @@ -249,7 +333,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data) { struct chan *chan = data; struct line *line = chan->line; - struct tty_struct *tty; + struct tty_struct *tty = line->tty; int err; /* @@ -268,42 +352,68 @@ static irqreturn_t line_write_interrupt(int irq, void *data) } spin_unlock(&line->lock); - tty = tty_port_tty_get(&line->port); if (tty == NULL) return IRQ_NONE; tty_wakeup(tty); - tty_kref_put(tty); - return IRQ_HANDLED; } int line_setup_irq(int fd, int input, int output, struct line *line, void *data) { const struct line_driver *driver = line->driver; - int err = 0; + int err = 0, flags = IRQF_SHARED | IRQF_SAMPLE_RANDOM; if (input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, - line_interrupt, IRQF_SHARED, - driver->read_irq_name, data); + line_interrupt, flags, + driver->read_irq_name, data); if (err) return err; if (output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, - line_write_interrupt, IRQF_SHARED, - driver->write_irq_name, data); + line_write_interrupt, flags, + driver->write_irq_name, data); return err; } -static int line_activate(struct tty_port *port, struct tty_struct *tty) +/* + * Normally, a driver like this can rely mostly on the tty layer + * locking, particularly when it comes to the driver structure. + * However, in this case, mconsole requests can come in "from the + * side", and race with opens and closes. + * + * mconsole config requests will want to be sure the device isn't in + * use, and get_config, open, and close will want a stable + * configuration. The checking and modification of the configuration + * is done under a spinlock. Checking whether the device is in use is + * line->tty->count > 1, also under the spinlock. + * + * line->count serves to decide whether the device should be enabled or + * disabled on the host. If it's equal to 0, then we are doing the + * first open or last close. Otherwise, open and close just return. + */ + +int line_open(struct line *lines, struct tty_struct *tty) { - int ret; - struct line *line = tty->driver_data; + struct line *line = &lines[tty->index]; + int err = -ENODEV; + + mutex_lock(&line->count_lock); + if (!line->valid) + goto out_unlock; - ret = enable_chan(line); - if (ret) - return ret; + err = 0; + if (line->count++) + goto out_unlock; + + BUG_ON(tty->driver_data); + tty->driver_data = line; + line->tty = tty; + + err = enable_chan(line); + if (err) /* line_close() will be called by our caller */ + goto out_unlock; if (!line->sigio) { chan_enable_winch(line->chan_out, tty); @@ -311,60 +421,44 @@ static int line_activate(struct tty_port *port, struct tty_struct *tty) } chan_window_size(line, &tty->winsize.ws_row, - &tty->winsize.ws_col); - - return 0; + &tty->winsize.ws_col); +out_unlock: + mutex_unlock(&line->count_lock); + return err; } -static const struct tty_port_operations line_port_ops = { - .activate = line_activate, -}; +static void unregister_winch(struct tty_struct *tty); -int line_open(struct tty_struct *tty, struct file *filp) +void line_close(struct tty_struct *tty, struct file * filp) { struct line *line = tty->driver_data; - return tty_port_open(&line->port, tty, filp); -} - -int line_install(struct tty_driver *driver, struct tty_struct *tty, - struct line *line) -{ - int ret; - - ret = tty_standard_install(driver, tty); - if (ret) - return ret; + /* + * If line_open fails (and tty->driver_data is never set), + * tty_open will call line_close. So just return in this case. + */ + if (line == NULL) + return; - tty->driver_data = line; + /* We ignore the error anyway! */ + flush_buffer(line); - return 0; -} + mutex_lock(&line->count_lock); + BUG_ON(!line->valid); -static void unregister_winch(struct tty_struct *tty); + if (--line->count) + goto out_unlock; -void line_cleanup(struct tty_struct *tty) -{ - struct line *line = tty->driver_data; + line->tty = NULL; + tty->driver_data = NULL; if (line->sigio) { unregister_winch(tty); line->sigio = 0; } -} - -void line_close(struct tty_struct *tty, struct file * filp) -{ - struct line *line = tty->driver_data; - tty_port_close(&line->port, tty, filp); -} - -void line_hangup(struct tty_struct *tty) -{ - struct line *line = tty->driver_data; - - tty_port_hangup(&line->port); +out_unlock: + mutex_unlock(&line->count_lock); } void close_lines(struct line *lines, int nlines) @@ -382,7 +476,9 @@ int setup_one_line(struct line *lines, int n, char *init, struct tty_driver *driver = line->driver->driver; int err = -EINVAL; - if (line->port.count) { + mutex_lock(&line->count_lock); + + if (line->count) { *error_out = "Device is already open"; goto out; } @@ -423,6 +519,7 @@ int setup_one_line(struct line *lines, int n, char *init, } } out: + mutex_unlock(&line->count_lock); return err; } @@ -510,17 +607,13 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str, line = &lines[dev]; + mutex_lock(&line->count_lock); if (!line->valid) CONFIG_CHUNK(str, size, n, "none", 1); - else { - struct tty_struct *tty = tty_port_tty_get(&line->port); - if (tty == NULL) { - CONFIG_CHUNK(str, size, n, line->init_str, 1); - } else { - n = chan_config_string(line, str, size, error_out); - tty_kref_put(tty); - } - } + else if (line->tty == NULL) + CONFIG_CHUNK(str, size, n, line->init_str, 1); + else n = chan_config_string(line, str, size, error_out); + mutex_unlock(&line->count_lock); return n; } @@ -570,9 +663,8 @@ int register_lines(struct line_driver *line_driver, driver->init_termios = tty_std_termios; for (i = 0; i < nlines; i++) { - tty_port_init(&lines[i].port); - lines[i].port.ops = &line_port_ops; spin_lock_init(&lines[i].lock); + mutex_init(&lines[i].count_lock); lines[i].driver = line_driver; INIT_LIST_HEAD(&lines[i].chan_list); } @@ -687,7 +779,8 @@ void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty, .stack = stack }); if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, - IRQF_SHARED, "winch", winch) < 0) { + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + "winch", winch) < 0) { printk(KERN_ERR "register_winch_irq - failed to register " "IRQ\n"); goto out_free; diff --git a/trunk/arch/um/drivers/line.h b/trunk/arch/um/drivers/line.h index bae95611e7ab..0a1834719dba 100644 --- a/trunk/arch/um/drivers/line.h +++ b/trunk/arch/um/drivers/line.h @@ -32,7 +32,9 @@ struct line_driver { }; struct line { - struct tty_port port; + struct tty_struct *tty; + struct mutex count_lock; + unsigned long count; int valid; char *init_str; @@ -57,11 +59,7 @@ struct line { }; extern void line_close(struct tty_struct *tty, struct file * filp); -extern int line_open(struct tty_struct *tty, struct file *filp); -extern int line_install(struct tty_driver *driver, struct tty_struct *tty, - struct line *line); -extern void line_cleanup(struct tty_struct *tty); -extern void line_hangup(struct tty_struct *tty); +extern int line_open(struct line *lines, struct tty_struct *tty); extern int line_setup(char **conf, unsigned nlines, char **def, char *init, char *name); extern int line_write(struct tty_struct *tty, const unsigned char *buf, @@ -72,6 +70,8 @@ extern int line_chars_in_buffer(struct tty_struct *tty); extern void line_flush_buffer(struct tty_struct *tty); extern void line_flush_chars(struct tty_struct *tty); extern int line_write_room(struct tty_struct *tty); +extern int line_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg); extern void line_throttle(struct tty_struct *tty); extern void line_unthrottle(struct tty_struct *tty); diff --git a/trunk/arch/um/drivers/mconsole_kern.c b/trunk/arch/um/drivers/mconsole_kern.c index 664a60e8dfb4..43b39d61b538 100644 --- a/trunk/arch/um/drivers/mconsole_kern.c +++ b/trunk/arch/um/drivers/mconsole_kern.c @@ -774,7 +774,8 @@ static int __init mconsole_init(void) register_reboot_notifier(&reboot_notifier); err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt, - IRQF_SHARED, "mconsole", (void *)sock); + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + "mconsole", (void *)sock); if (err) { printk(KERN_ERR "Failed to get IRQ for management console\n"); goto out; diff --git a/trunk/arch/um/drivers/port_kern.c b/trunk/arch/um/drivers/port_kern.c index 1d83d50236e1..11866ffd45a9 100644 --- a/trunk/arch/um/drivers/port_kern.c +++ b/trunk/arch/um/drivers/port_kern.c @@ -100,7 +100,8 @@ static int port_accept(struct port_list *port) .port = port }); if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, - IRQF_SHARED, "telnetd", conn)) { + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + "telnetd", conn)) { printk(KERN_ERR "port_accept : failed to get IRQ for " "telnetd\n"); goto out_free; @@ -183,7 +184,8 @@ void *port_data(int port_num) } if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, - IRQF_SHARED, "port", port)) { + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + "port", port)) { printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num); goto out_close; } diff --git a/trunk/arch/um/drivers/random.c b/trunk/arch/um/drivers/random.c index e32c6aa6396f..b25296e6218a 100644 --- a/trunk/arch/um/drivers/random.c +++ b/trunk/arch/um/drivers/random.c @@ -131,7 +131,8 @@ static int __init rng_init (void) random_fd = err; err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt, - 0, "random", NULL); + IRQF_SAMPLE_RANDOM, "random", + NULL); if (err) goto err_out_cleanup_hw; diff --git a/trunk/arch/um/drivers/ssl.c b/trunk/arch/um/drivers/ssl.c index 7e86f0070123..e09801a1327b 100644 --- a/trunk/arch/um/drivers/ssl.c +++ b/trunk/arch/um/drivers/ssl.c @@ -87,13 +87,40 @@ static int ssl_remove(int n, char **error_out) error_out); } -static int ssl_install(struct tty_driver *driver, struct tty_struct *tty) +static int ssl_open(struct tty_struct *tty, struct file *filp) +{ + int err = line_open(serial_lines, tty); + + if (err) + printk(KERN_ERR "Failed to open serial line %d, err = %d\n", + tty->index, err); + + return err; +} + +#if 0 +static void ssl_flush_buffer(struct tty_struct *tty) +{ + return; +} + +static void ssl_stop(struct tty_struct *tty) +{ + printk(KERN_ERR "Someone should implement ssl_stop\n"); +} + +static void ssl_start(struct tty_struct *tty) +{ + printk(KERN_ERR "Someone should implement ssl_start\n"); +} + +void ssl_hangup(struct tty_struct *tty) { - return line_install(driver, tty, &serial_lines[tty->index]); } +#endif static const struct tty_operations ssl_ops = { - .open = line_open, + .open = ssl_open, .close = line_close, .write = line_write, .put_char = line_put_char, @@ -102,11 +129,14 @@ static const struct tty_operations ssl_ops = { .flush_buffer = line_flush_buffer, .flush_chars = line_flush_chars, .set_termios = line_set_termios, + .ioctl = line_ioctl, .throttle = line_throttle, .unthrottle = line_unthrottle, - .install = ssl_install, - .cleanup = line_cleanup, - .hangup = line_hangup, +#if 0 + .stop = ssl_stop, + .start = ssl_start, + .hangup = ssl_hangup, +#endif }; /* Changed by ssl_init and referenced by ssl_exit, which are both serialized diff --git a/trunk/arch/um/drivers/stdio_console.c b/trunk/arch/um/drivers/stdio_console.c index 929b99a261f3..7663541c372e 100644 --- a/trunk/arch/um/drivers/stdio_console.c +++ b/trunk/arch/um/drivers/stdio_console.c @@ -89,17 +89,21 @@ static int con_remove(int n, char **error_out) return line_remove(vts, ARRAY_SIZE(vts), n, error_out); } -/* Set in an initcall, checked in an exitcall */ -static int con_init_done = 0; - -static int con_install(struct tty_driver *driver, struct tty_struct *tty) +static int con_open(struct tty_struct *tty, struct file *filp) { - return line_install(driver, tty, &vts[tty->index]); + int err = line_open(vts, tty); + if (err) + printk(KERN_ERR "Failed to open console %d, err = %d\n", + tty->index, err); + + return err; } +/* Set in an initcall, checked in an exitcall */ +static int con_init_done = 0; + static const struct tty_operations console_ops = { - .open = line_open, - .install = con_install, + .open = con_open, .close = line_close, .write = line_write, .put_char = line_put_char, @@ -108,10 +112,9 @@ static const struct tty_operations console_ops = { .flush_buffer = line_flush_buffer, .flush_chars = line_flush_chars, .set_termios = line_set_termios, + .ioctl = line_ioctl, .throttle = line_throttle, .unthrottle = line_unthrottle, - .cleanup = line_cleanup, - .hangup = line_hangup, }; static void uml_console_write(struct console *console, const char *string, diff --git a/trunk/arch/um/drivers/ubd_kern.c b/trunk/arch/um/drivers/ubd_kern.c index 0643e5bc9f41..20505cafa299 100644 --- a/trunk/arch/um/drivers/ubd_kern.c +++ b/trunk/arch/um/drivers/ubd_kern.c @@ -514,7 +514,7 @@ static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out) goto out; } - fd = os_open_file(ubd_dev->file, of_read(OPENFLAGS()), 0); + fd = os_open_file(ubd_dev->file, global_openflags, 0); if (fd < 0) return fd; diff --git a/trunk/arch/um/drivers/xterm_kern.c b/trunk/arch/um/drivers/xterm_kern.c index e3031e69445d..b68bbe269e01 100644 --- a/trunk/arch/um/drivers/xterm_kern.c +++ b/trunk/arch/um/drivers/xterm_kern.c @@ -50,7 +50,8 @@ int xterm_fd(int socket, int *pid_out) init_completion(&data->ready); err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt, - IRQF_SHARED, "xterm", data); + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + "xterm", data); if (err) { printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, " "err = %d\n", err); diff --git a/trunk/arch/um/include/asm/ptrace-generic.h b/trunk/arch/um/include/asm/ptrace-generic.h index 442f1d025dc2..e786a6a3ec5e 100644 --- a/trunk/arch/um/include/asm/ptrace-generic.h +++ b/trunk/arch/um/include/asm/ptrace-generic.h @@ -37,8 +37,6 @@ extern int putreg(struct task_struct *child, int regno, unsigned long value); extern int arch_copy_tls(struct task_struct *new); extern void clear_flushed_tls(struct task_struct *task); -extern void syscall_trace_enter(struct pt_regs *regs); -extern void syscall_trace_leave(struct pt_regs *regs); #endif diff --git a/trunk/arch/um/include/shared/as-layout.h b/trunk/arch/um/include/shared/as-layout.h index 86daa5461815..896e16602176 100644 --- a/trunk/arch/um/include/shared/as-layout.h +++ b/trunk/arch/um/include/shared/as-layout.h @@ -60,8 +60,7 @@ extern unsigned long host_task_size; extern int linux_main(int argc, char **argv); -struct siginfo; -extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *); +extern void (*sig_info[])(int, struct uml_pt_regs *); #endif diff --git a/trunk/arch/um/include/shared/irq_user.h b/trunk/arch/um/include/shared/irq_user.h index 2b6d703925b5..c6c784df2673 100644 --- a/trunk/arch/um/include/shared/irq_user.h +++ b/trunk/arch/um/include/shared/irq_user.h @@ -20,8 +20,7 @@ struct irq_fd { enum { IRQ_READ, IRQ_WRITE }; -struct siginfo; -extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); +extern void sigio_handler(int sig, struct uml_pt_regs *regs); extern void free_irq_by_fd(int fd); extern void reactivate_fd(int fd, int irqnum); extern void deactivate_fd(int fd, int irqnum); diff --git a/trunk/arch/um/include/shared/kern_util.h b/trunk/arch/um/include/shared/kern_util.h index af6b6dc868ba..00965d06d2ca 100644 --- a/trunk/arch/um/include/shared/kern_util.h +++ b/trunk/arch/um/include/shared/kern_util.h @@ -9,8 +9,6 @@ #include "sysdep/ptrace.h" #include "sysdep/faultinfo.h" -struct siginfo; - extern int uml_exitcode; extern int ncpus; @@ -24,7 +22,7 @@ extern void free_stack(unsigned long stack, int order); extern int do_signal(void); extern void interrupt_end(void); -extern void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs); +extern void relay_signal(int sig, struct uml_pt_regs *regs); extern unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, struct uml_pt_regs *regs); @@ -35,8 +33,9 @@ extern unsigned int do_IRQ(int irq, struct uml_pt_regs *regs); extern int smp_sigio_handler(void); extern void initial_thread_cb(void (*proc)(void *), void *arg); extern int is_syscall(unsigned long addr); +extern void timer_handler(int sig, struct uml_pt_regs *regs); -extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); +extern void timer_handler(int sig, struct uml_pt_regs *regs); extern int start_uml(void); extern void paging_init(void); @@ -60,9 +59,9 @@ extern unsigned long from_irq_stack(int nested); extern void syscall_trace(struct uml_pt_regs *regs, int entryexit); extern int singlestepping(void *t); -extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); -extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs); -extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); +extern void segv_handler(int sig, struct uml_pt_regs *regs); +extern void bus_handler(int sig, struct uml_pt_regs *regs); +extern void winch(int sig, struct uml_pt_regs *regs); extern void fatal_sigsegv(void) __attribute__ ((noreturn)); diff --git a/trunk/arch/um/kernel/irq.c b/trunk/arch/um/kernel/irq.c index 9883026f0730..00506c3d5d6e 100644 --- a/trunk/arch/um/kernel/irq.c +++ b/trunk/arch/um/kernel/irq.c @@ -30,7 +30,7 @@ static struct irq_fd **last_irq_ptr = &active_fds; extern void free_irqs(void); -void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) +void sigio_handler(int sig, struct uml_pt_regs *regs) { struct irq_fd *irq_fd; int n; diff --git a/trunk/arch/um/kernel/process.c b/trunk/arch/um/kernel/process.c index 57fc7028714a..ccb9a9d283f1 100644 --- a/trunk/arch/um/kernel/process.c +++ b/trunk/arch/um/kernel/process.c @@ -151,10 +151,12 @@ void new_thread_handler(void) * 0 if it just exits */ n = run_kernel_thread(fn, arg, ¤t->thread.exec_buf); - if (n == 1) + if (n == 1) { + /* Handle any immediate reschedules or signals */ + interrupt_end(); userspace(¤t->thread.regs.regs); - else - do_exit(0); + } + else do_exit(0); } /* Called magically, see new_thread_handler above */ @@ -173,6 +175,9 @@ void fork_handler(void) current->thread.prev_sched = NULL; + /* Handle any immediate reschedules or signals */ + interrupt_end(); + userspace(¤t->thread.regs.regs); } @@ -188,7 +193,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, if (current->thread.forking) { memcpy(&p->thread.regs.regs, ®s->regs, sizeof(p->thread.regs.regs)); - PT_REGS_SET_SYSCALL_RETURN(&p->thread.regs, 0); + UPT_SET_SYSCALL_RETURN(&p->thread.regs.regs, 0); if (sp != 0) REGS_SP(p->thread.regs.regs.gp) = sp; diff --git a/trunk/arch/um/kernel/ptrace.c b/trunk/arch/um/kernel/ptrace.c index 694d551c8899..06b190390505 100644 --- a/trunk/arch/um/kernel/ptrace.c +++ b/trunk/arch/um/kernel/ptrace.c @@ -3,12 +3,11 @@ * Licensed under the GPL */ -#include -#include -#include -#include -#include -#include +#include "linux/audit.h" +#include "linux/ptrace.h" +#include "linux/sched.h" +#include "asm/uaccess.h" +#include "skas_ptrace.h" @@ -163,36 +162,48 @@ static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs, * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check */ -void syscall_trace_enter(struct pt_regs *regs) +void syscall_trace(struct uml_pt_regs *regs, int entryexit) { - audit_syscall_entry(HOST_AUDIT_ARCH, - UPT_SYSCALL_NR(®s->regs), - UPT_SYSCALL_ARG1(®s->regs), - UPT_SYSCALL_ARG2(®s->regs), - UPT_SYSCALL_ARG3(®s->regs), - UPT_SYSCALL_ARG4(®s->regs)); - - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - - tracehook_report_syscall_entry(regs); -} - -void syscall_trace_leave(struct pt_regs *regs) -{ - int ptraced = current->ptrace; - - audit_syscall_exit(regs); + int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit; + int tracesysgood; + + if (!entryexit) + audit_syscall_entry(HOST_AUDIT_ARCH, + UPT_SYSCALL_NR(regs), + UPT_SYSCALL_ARG1(regs), + UPT_SYSCALL_ARG2(regs), + UPT_SYSCALL_ARG3(regs), + UPT_SYSCALL_ARG4(regs)); + else + audit_syscall_exit(regs); /* Fake a debug trap */ - if (ptraced & PT_DTRACE) - send_sigtrap(current, ®s->regs, 0); + if (is_singlestep) + send_sigtrap(current, regs, 0); if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; - tracehook_report_syscall_exit(regs, 0); - /* force do_signal() --> is_syscall() */ - if (ptraced & PT_PTRACED) + if (!(current->ptrace & PT_PTRACED)) + return; + + /* + * the 0x80 provides a way for the tracing parent to distinguish + * between a syscall stop and SIGTRAP delivery + */ + tracesysgood = (current->ptrace & PT_TRACESYSGOOD); + ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0)); + + if (entryexit) /* force do_signal() --> is_syscall() */ set_thread_flag(TIF_SIGPENDING); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } } diff --git a/trunk/arch/um/kernel/sigio.c b/trunk/arch/um/kernel/sigio.c index c88211139a51..2a1639255763 100644 --- a/trunk/arch/um/kernel/sigio.c +++ b/trunk/arch/um/kernel/sigio.c @@ -25,7 +25,8 @@ int write_sigio_irq(int fd) int err; err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt, - 0, "write sigio", NULL); + IRQF_SAMPLE_RANDOM, "write sigio", + NULL); if (err) { printk(KERN_ERR "write_sigio_irq : um_request_irq failed, " "err = %d\n", err); diff --git a/trunk/arch/um/kernel/skas/syscall.c b/trunk/arch/um/kernel/skas/syscall.c index 86368a025a96..05fbeb480e0b 100644 --- a/trunk/arch/um/kernel/skas/syscall.c +++ b/trunk/arch/um/kernel/skas/syscall.c @@ -18,7 +18,7 @@ void handle_syscall(struct uml_pt_regs *r) long result; int syscall; - syscall_trace_enter(regs); + syscall_trace(r, 0); /* * This should go in the declaration of syscall, but when I do that, @@ -34,7 +34,7 @@ void handle_syscall(struct uml_pt_regs *r) result = -ENOSYS; else result = EXECUTE_SYSCALL(syscall, regs); - PT_REGS_SET_SYSCALL_RETURN(regs, result); + UPT_SET_SYSCALL_RETURN(r, result); - syscall_trace_leave(regs); + syscall_trace(r, 1); } diff --git a/trunk/arch/um/kernel/time.c b/trunk/arch/um/kernel/time.c index 5f76d4ba151c..d1a23fb3190d 100644 --- a/trunk/arch/um/kernel/time.c +++ b/trunk/arch/um/kernel/time.c @@ -13,7 +13,7 @@ #include "kern_util.h" #include "os.h" -void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) +void timer_handler(int sig, struct uml_pt_regs *regs) { unsigned long flags; diff --git a/trunk/arch/um/kernel/trap.c b/trunk/arch/um/kernel/trap.c index 0353b98ae35a..3be60765c0e2 100644 --- a/trunk/arch/um/kernel/trap.c +++ b/trunk/arch/um/kernel/trap.c @@ -172,7 +172,7 @@ void fatal_sigsegv(void) os_dump_core(); } -void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) +void segv_handler(int sig, struct uml_pt_regs *regs) { struct faultinfo * fi = UPT_FAULTINFO(regs); @@ -258,11 +258,8 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, return 0; } -void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs) +void relay_signal(int sig, struct uml_pt_regs *regs) { - struct faultinfo *fi; - struct siginfo clean_si; - if (!UPT_IS_USER(regs)) { if (sig == SIGBUS) printk(KERN_ERR "Bus error - the host /dev/shm or /tmp " @@ -272,40 +269,18 @@ void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs) arch_examine_signal(sig, regs); - memset(&clean_si, 0, sizeof(clean_si)); - clean_si.si_signo = si->si_signo; - clean_si.si_errno = si->si_errno; - clean_si.si_code = si->si_code; - switch (sig) { - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - case SIGTRAP: - fi = UPT_FAULTINFO(regs); - clean_si.si_addr = (void __user *) FAULT_ADDRESS(*fi); - current->thread.arch.faultinfo = *fi; -#ifdef __ARCH_SI_TRAPNO - clean_si.si_trapno = si->si_trapno; -#endif - break; - default: - printk(KERN_ERR "Attempted to relay unknown signal %d (si_code = %d)\n", - sig, si->si_code); - } - - force_sig_info(sig, &clean_si, current); + current->thread.arch.faultinfo = *UPT_FAULTINFO(regs); + force_sig(sig, current); } -void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs) +void bus_handler(int sig, struct uml_pt_regs *regs) { if (current->thread.fault_catcher != NULL) UML_LONGJMP(current->thread.fault_catcher, 1); - else - relay_signal(sig, si, regs); + else relay_signal(sig, regs); } -void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) +void winch(int sig, struct uml_pt_regs *regs) { do_IRQ(WINCH_IRQ, regs); } diff --git a/trunk/arch/um/os-Linux/internal.h b/trunk/arch/um/os-Linux/internal.h index 0dc2c9f135f6..2c3c3ecd8c01 100644 --- a/trunk/arch/um/os-Linux/internal.h +++ b/trunk/arch/um/os-Linux/internal.h @@ -1 +1 @@ -void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc); +void alarm_handler(int, mcontext_t *); diff --git a/trunk/arch/um/os-Linux/signal.c b/trunk/arch/um/os-Linux/signal.c index 6366ce904b9b..2d22f1fcd8e2 100644 --- a/trunk/arch/um/os-Linux/signal.c +++ b/trunk/arch/um/os-Linux/signal.c @@ -13,9 +13,8 @@ #include "kern_util.h" #include "os.h" #include "sysdep/mcontext.h" -#include "internal.h" -void (*sig_info[NSIG])(int, siginfo_t *, struct uml_pt_regs *) = { +void (*sig_info[NSIG])(int, struct uml_pt_regs *) = { [SIGTRAP] = relay_signal, [SIGFPE] = relay_signal, [SIGILL] = relay_signal, @@ -25,7 +24,7 @@ void (*sig_info[NSIG])(int, siginfo_t *, struct uml_pt_regs *) = { [SIGIO] = sigio_handler, [SIGVTALRM] = timer_handler }; -static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc) +static void sig_handler_common(int sig, mcontext_t *mc) { struct uml_pt_regs r; int save_errno = errno; @@ -41,7 +40,7 @@ static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc) if ((sig != SIGIO) && (sig != SIGWINCH) && (sig != SIGVTALRM)) unblock_signals(); - (*sig_info[sig])(sig, si, &r); + (*sig_info[sig])(sig, &r); errno = save_errno; } @@ -61,7 +60,7 @@ static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc) static int signals_enabled; static unsigned int signals_pending; -void sig_handler(int sig, siginfo_t *si, mcontext_t *mc) +void sig_handler(int sig, mcontext_t *mc) { int enabled; @@ -73,7 +72,7 @@ void sig_handler(int sig, siginfo_t *si, mcontext_t *mc) block_signals(); - sig_handler_common(sig, si, mc); + sig_handler_common(sig, mc); set_signals(enabled); } @@ -86,10 +85,10 @@ static void real_alarm_handler(mcontext_t *mc) get_regs_from_mc(®s, mc); regs.is_user = 0; unblock_signals(); - timer_handler(SIGVTALRM, NULL, ®s); + timer_handler(SIGVTALRM, ®s); } -void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc) +void alarm_handler(int sig, mcontext_t *mc) { int enabled; @@ -120,7 +119,7 @@ void set_sigstack(void *sig_stack, int size) panic("enabling signal stack failed, errno = %d\n", errno); } -static void (*handlers[_NSIG])(int sig, siginfo_t *si, mcontext_t *mc) = { +static void (*handlers[_NSIG])(int sig, mcontext_t *mc) = { [SIGSEGV] = sig_handler, [SIGBUS] = sig_handler, [SIGILL] = sig_handler, @@ -133,7 +132,7 @@ static void (*handlers[_NSIG])(int sig, siginfo_t *si, mcontext_t *mc) = { }; -static void hard_handler(int sig, siginfo_t *si, void *p) +static void hard_handler(int sig, siginfo_t *info, void *p) { struct ucontext *uc = p; mcontext_t *mc = &uc->uc_mcontext; @@ -162,7 +161,7 @@ static void hard_handler(int sig, siginfo_t *si, void *p) while ((sig = ffs(pending)) != 0){ sig--; pending &= ~(1 << sig); - (*handlers[sig])(sig, si, mc); + (*handlers[sig])(sig, mc); } /* @@ -274,12 +273,9 @@ void unblock_signals(void) * Deal with SIGIO first because the alarm handler might * schedule, leaving the pending SIGIO stranded until we come * back here. - * - * SIGIO's handler doesn't use siginfo or mcontext, - * so they can be NULL. */ if (save_pending & SIGIO_MASK) - sig_handler_common(SIGIO, NULL, NULL); + sig_handler_common(SIGIO, NULL); if (save_pending & SIGVTALRM_MASK) real_alarm_handler(NULL); diff --git a/trunk/arch/um/os-Linux/skas/process.c b/trunk/arch/um/os-Linux/skas/process.c index d93bb40499f7..cd65727854eb 100644 --- a/trunk/arch/um/os-Linux/skas/process.c +++ b/trunk/arch/um/os-Linux/skas/process.c @@ -346,10 +346,6 @@ void userspace(struct uml_pt_regs *regs) int err, status, op, pid = userspace_pid[0]; /* To prevent races if using_sysemu changes under us.*/ int local_using_sysemu; - siginfo_t si; - - /* Handle any immediate reschedules or signals */ - interrupt_end(); if (getitimer(ITIMER_VIRTUAL, &timer)) printk(UM_KERN_ERR "Failed to get itimer, errno = %d\n", errno); @@ -408,17 +404,13 @@ void userspace(struct uml_pt_regs *regs) if (WIFSTOPPED(status)) { int sig = WSTOPSIG(status); - - ptrace(PTRACE_GETSIGINFO, pid, 0, &si); - switch (sig) { case SIGSEGV: if (PTRACE_FULL_FAULTINFO || !ptrace_faultinfo) { get_skas_faultinfo(pid, ®s->faultinfo); - (*sig_info[SIGSEGV])(SIGSEGV, &si, - regs); + (*sig_info[SIGSEGV])(SIGSEGV, regs); } else handle_segv(pid, regs); break; @@ -426,14 +418,14 @@ void userspace(struct uml_pt_regs *regs) handle_trap(pid, regs, local_using_sysemu); break; case SIGTRAP: - relay_signal(SIGTRAP, &si, regs); + relay_signal(SIGTRAP, regs); break; case SIGVTALRM: now = os_nsecs(); if (now < nsecs) break; block_signals(); - (*sig_info[sig])(sig, &si, regs); + (*sig_info[sig])(sig, regs); unblock_signals(); nsecs = timer.it_value.tv_sec * UM_NSEC_PER_SEC + @@ -447,7 +439,7 @@ void userspace(struct uml_pt_regs *regs) case SIGFPE: case SIGWINCH: block_signals(); - (*sig_info[sig])(sig, &si, regs); + (*sig_info[sig])(sig, regs); unblock_signals(); break; default: diff --git a/trunk/arch/um/os-Linux/time.c b/trunk/arch/um/os-Linux/time.c index f60238559af3..910499d76a67 100644 --- a/trunk/arch/um/os-Linux/time.c +++ b/trunk/arch/um/os-Linux/time.c @@ -87,7 +87,7 @@ static int after_sleep_interval(struct timespec *ts) static void deliver_alarm(void) { - alarm_handler(SIGVTALRM, NULL, NULL); + alarm_handler(SIGVTALRM, NULL); } static unsigned long long sleep_time(unsigned long long nsecs) diff --git a/trunk/arch/x86/include/asm/perf_event.h b/trunk/arch/x86/include/asm/perf_event.h index dab39350e51e..c78f14a0df00 100644 --- a/trunk/arch/x86/include/asm/perf_event.h +++ b/trunk/arch/x86/include/asm/perf_event.h @@ -234,7 +234,7 @@ extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr); extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap); extern void perf_check_microcode(void); #else -static inline struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr) +static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr) { *nr = 0; return NULL; diff --git a/trunk/arch/x86/kernel/cpu/perf_event.h b/trunk/arch/x86/kernel/cpu/perf_event.h index 821d53b696d1..a15df4be151f 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event.h +++ b/trunk/arch/x86/kernel/cpu/perf_event.h @@ -374,7 +374,7 @@ struct x86_pmu { /* * Intel DebugStore bits */ - unsigned int bts :1, + int bts :1, bts_active :1, pebs :1, pebs_active :1, diff --git a/trunk/arch/x86/kernel/cpu/perf_event_intel.c b/trunk/arch/x86/kernel/cpu/perf_event_intel.c index 382366977d4c..7a8b9d0abcaa 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event_intel.c +++ b/trunk/arch/x86/kernel/cpu/perf_event_intel.c @@ -138,84 +138,6 @@ static u64 intel_pmu_event_map(int hw_event) return intel_perfmon_event_map[hw_event]; } -#define SNB_DMND_DATA_RD (1ULL << 0) -#define SNB_DMND_RFO (1ULL << 1) -#define SNB_DMND_IFETCH (1ULL << 2) -#define SNB_DMND_WB (1ULL << 3) -#define SNB_PF_DATA_RD (1ULL << 4) -#define SNB_PF_RFO (1ULL << 5) -#define SNB_PF_IFETCH (1ULL << 6) -#define SNB_LLC_DATA_RD (1ULL << 7) -#define SNB_LLC_RFO (1ULL << 8) -#define SNB_LLC_IFETCH (1ULL << 9) -#define SNB_BUS_LOCKS (1ULL << 10) -#define SNB_STRM_ST (1ULL << 11) -#define SNB_OTHER (1ULL << 15) -#define SNB_RESP_ANY (1ULL << 16) -#define SNB_NO_SUPP (1ULL << 17) -#define SNB_LLC_HITM (1ULL << 18) -#define SNB_LLC_HITE (1ULL << 19) -#define SNB_LLC_HITS (1ULL << 20) -#define SNB_LLC_HITF (1ULL << 21) -#define SNB_LOCAL (1ULL << 22) -#define SNB_REMOTE (0xffULL << 23) -#define SNB_SNP_NONE (1ULL << 31) -#define SNB_SNP_NOT_NEEDED (1ULL << 32) -#define SNB_SNP_MISS (1ULL << 33) -#define SNB_NO_FWD (1ULL << 34) -#define SNB_SNP_FWD (1ULL << 35) -#define SNB_HITM (1ULL << 36) -#define SNB_NON_DRAM (1ULL << 37) - -#define SNB_DMND_READ (SNB_DMND_DATA_RD|SNB_LLC_DATA_RD) -#define SNB_DMND_WRITE (SNB_DMND_RFO|SNB_LLC_RFO) -#define SNB_DMND_PREFETCH (SNB_PF_DATA_RD|SNB_PF_RFO) - -#define SNB_SNP_ANY (SNB_SNP_NONE|SNB_SNP_NOT_NEEDED| \ - SNB_SNP_MISS|SNB_NO_FWD|SNB_SNP_FWD| \ - SNB_HITM) - -#define SNB_DRAM_ANY (SNB_LOCAL|SNB_REMOTE|SNB_SNP_ANY) -#define SNB_DRAM_REMOTE (SNB_REMOTE|SNB_SNP_ANY) - -#define SNB_L3_ACCESS SNB_RESP_ANY -#define SNB_L3_MISS (SNB_DRAM_ANY|SNB_NON_DRAM) - -static __initconst const u64 snb_hw_cache_extra_regs - [PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX] = -{ - [ C(LL ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = SNB_DMND_READ|SNB_L3_ACCESS, - [ C(RESULT_MISS) ] = SNB_DMND_READ|SNB_L3_MISS, - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = SNB_DMND_WRITE|SNB_L3_ACCESS, - [ C(RESULT_MISS) ] = SNB_DMND_WRITE|SNB_L3_MISS, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = SNB_DMND_PREFETCH|SNB_L3_ACCESS, - [ C(RESULT_MISS) ] = SNB_DMND_PREFETCH|SNB_L3_MISS, - }, - }, - [ C(NODE) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = SNB_DMND_READ|SNB_DRAM_ANY, - [ C(RESULT_MISS) ] = SNB_DMND_READ|SNB_DRAM_REMOTE, - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = SNB_DMND_WRITE|SNB_DRAM_ANY, - [ C(RESULT_MISS) ] = SNB_DMND_WRITE|SNB_DRAM_REMOTE, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = SNB_DMND_PREFETCH|SNB_DRAM_ANY, - [ C(RESULT_MISS) ] = SNB_DMND_PREFETCH|SNB_DRAM_REMOTE, - }, - }, -}; - static __initconst const u64 snb_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] @@ -313,16 +235,16 @@ static __initconst const u64 snb_hw_cache_event_ids }, [ C(NODE) ] = { [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x01b7, - [ C(RESULT_MISS) ] = 0x01b7, + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, }, [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x01b7, - [ C(RESULT_MISS) ] = 0x01b7, + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, }, [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x01b7, - [ C(RESULT_MISS) ] = 0x01b7, + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, }, }, @@ -2042,8 +1964,6 @@ __init int intel_pmu_init(void) case 58: /* IvyBridge */ memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, sizeof(hw_cache_event_ids)); - memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs, - sizeof(hw_cache_extra_regs)); intel_pmu_lbr_init_snb(); diff --git a/trunk/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/trunk/arch/x86/kernel/cpu/perf_event_intel_uncore.c index 7563fda9f033..19faffc60886 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/trunk/arch/x86/kernel/cpu/perf_event_intel_uncore.c @@ -18,7 +18,6 @@ static struct event_constraint constraint_empty = EVENT_CONSTRAINT(0, 0, 0); DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7"); -DEFINE_UNCORE_FORMAT_ATTR(event_ext, event, "config:0-7,21"); DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15"); DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18"); DEFINE_UNCORE_FORMAT_ATTR(tid_en, tid_en, "config:19"); @@ -34,81 +33,10 @@ DEFINE_UNCORE_FORMAT_ATTR(filter_tid, filter_tid, "config1:0-4"); DEFINE_UNCORE_FORMAT_ATTR(filter_nid, filter_nid, "config1:10-17"); DEFINE_UNCORE_FORMAT_ATTR(filter_state, filter_state, "config1:18-22"); DEFINE_UNCORE_FORMAT_ATTR(filter_opc, filter_opc, "config1:23-31"); -DEFINE_UNCORE_FORMAT_ATTR(filter_band0, filter_band0, "config1:0-7"); -DEFINE_UNCORE_FORMAT_ATTR(filter_band1, filter_band1, "config1:8-15"); -DEFINE_UNCORE_FORMAT_ATTR(filter_band2, filter_band2, "config1:16-23"); -DEFINE_UNCORE_FORMAT_ATTR(filter_band3, filter_band3, "config1:24-31"); - -static u64 uncore_msr_read_counter(struct intel_uncore_box *box, struct perf_event *event) -{ - u64 count; - - rdmsrl(event->hw.event_base, count); - - return count; -} - -/* - * generic get constraint function for shared match/mask registers. - */ -static struct event_constraint * -uncore_get_constraint(struct intel_uncore_box *box, struct perf_event *event) -{ - struct intel_uncore_extra_reg *er; - struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; - struct hw_perf_event_extra *reg2 = &event->hw.branch_reg; - unsigned long flags; - bool ok = false; - - /* - * reg->alloc can be set due to existing state, so for fake box we - * need to ignore this, otherwise we might fail to allocate proper - * fake state for this extra reg constraint. - */ - if (reg1->idx == EXTRA_REG_NONE || - (!uncore_box_is_fake(box) && reg1->alloc)) - return NULL; - - er = &box->shared_regs[reg1->idx]; - raw_spin_lock_irqsave(&er->lock, flags); - if (!atomic_read(&er->ref) || - (er->config1 == reg1->config && er->config2 == reg2->config)) { - atomic_inc(&er->ref); - er->config1 = reg1->config; - er->config2 = reg2->config; - ok = true; - } - raw_spin_unlock_irqrestore(&er->lock, flags); - - if (ok) { - if (!uncore_box_is_fake(box)) - reg1->alloc = 1; - return NULL; - } - - return &constraint_empty; -} - -static void uncore_put_constraint(struct intel_uncore_box *box, struct perf_event *event) -{ - struct intel_uncore_extra_reg *er; - struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; - - /* - * Only put constraint if extra reg was actually allocated. Also - * takes care of event which do not use an extra shared reg. - * - * Also, if this is a fake box we shouldn't touch any event state - * (reg->alloc) and we don't care about leaving inconsistent box - * state either since it will be thrown out. - */ - if (uncore_box_is_fake(box) || !reg1->alloc) - return; - - er = &box->shared_regs[reg1->idx]; - atomic_dec(&er->ref); - reg1->alloc = 0; -} +DEFINE_UNCORE_FORMAT_ATTR(filter_brand0, filter_brand0, "config1:0-7"); +DEFINE_UNCORE_FORMAT_ATTR(filter_brand1, filter_brand1, "config1:8-15"); +DEFINE_UNCORE_FORMAT_ATTR(filter_brand2, filter_brand2, "config1:16-23"); +DEFINE_UNCORE_FORMAT_ATTR(filter_brand3, filter_brand3, "config1:24-31"); /* Sandy Bridge-EP uncore support */ static struct intel_uncore_type snbep_uncore_cbox; @@ -136,15 +64,18 @@ static void snbep_uncore_pci_enable_box(struct intel_uncore_box *box) pci_write_config_dword(pdev, box_ctl, config); } -static void snbep_uncore_pci_enable_event(struct intel_uncore_box *box, struct perf_event *event) +static void snbep_uncore_pci_enable_event(struct intel_uncore_box *box, + struct perf_event *event) { struct pci_dev *pdev = box->pci_dev; struct hw_perf_event *hwc = &event->hw; - pci_write_config_dword(pdev, hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN); + pci_write_config_dword(pdev, hwc->config_base, hwc->config | + SNBEP_PMON_CTL_EN); } -static void snbep_uncore_pci_disable_event(struct intel_uncore_box *box, struct perf_event *event) +static void snbep_uncore_pci_disable_event(struct intel_uncore_box *box, + struct perf_event *event) { struct pci_dev *pdev = box->pci_dev; struct hw_perf_event *hwc = &event->hw; @@ -152,7 +83,8 @@ static void snbep_uncore_pci_disable_event(struct intel_uncore_box *box, struct pci_write_config_dword(pdev, hwc->config_base, hwc->config); } -static u64 snbep_uncore_pci_read_counter(struct intel_uncore_box *box, struct perf_event *event) +static u64 snbep_uncore_pci_read_counter(struct intel_uncore_box *box, + struct perf_event *event) { struct pci_dev *pdev = box->pci_dev; struct hw_perf_event *hwc = &event->hw; @@ -160,15 +92,14 @@ static u64 snbep_uncore_pci_read_counter(struct intel_uncore_box *box, struct pe pci_read_config_dword(pdev, hwc->event_base, (u32 *)&count); pci_read_config_dword(pdev, hwc->event_base + 4, (u32 *)&count + 1); - return count; } static void snbep_uncore_pci_init_box(struct intel_uncore_box *box) { struct pci_dev *pdev = box->pci_dev; - - pci_write_config_dword(pdev, SNBEP_PCI_PMON_BOX_CTL, SNBEP_PMON_BOX_CTL_INT); + pci_write_config_dword(pdev, SNBEP_PCI_PMON_BOX_CTL, + SNBEP_PMON_BOX_CTL_INT); } static void snbep_uncore_msr_disable_box(struct intel_uncore_box *box) @@ -181,6 +112,7 @@ static void snbep_uncore_msr_disable_box(struct intel_uncore_box *box) rdmsrl(msr, config); config |= SNBEP_PMON_BOX_CTL_FRZ; wrmsrl(msr, config); + return; } } @@ -194,10 +126,12 @@ static void snbep_uncore_msr_enable_box(struct intel_uncore_box *box) rdmsrl(msr, config); config &= ~SNBEP_PMON_BOX_CTL_FRZ; wrmsrl(msr, config); + return; } } -static void snbep_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) +static void snbep_uncore_msr_enable_event(struct intel_uncore_box *box, + struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct hw_perf_event_extra *reg1 = &hwc->extra_reg; @@ -216,15 +150,68 @@ static void snbep_uncore_msr_disable_event(struct intel_uncore_box *box, wrmsrl(hwc->config_base, hwc->config); } +static u64 snbep_uncore_msr_read_counter(struct intel_uncore_box *box, + struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u64 count; + + rdmsrl(hwc->event_base, count); + return count; +} + static void snbep_uncore_msr_init_box(struct intel_uncore_box *box) { unsigned msr = uncore_msr_box_ctl(box); - if (msr) wrmsrl(msr, SNBEP_PMON_BOX_CTL_INT); } -static int snbep_uncore_hw_config(struct intel_uncore_box *box, struct perf_event *event) +static struct event_constraint * +snbep_uncore_get_constraint(struct intel_uncore_box *box, + struct perf_event *event) +{ + struct intel_uncore_extra_reg *er; + struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; + unsigned long flags; + bool ok = false; + + if (reg1->idx == EXTRA_REG_NONE || (box->phys_id >= 0 && reg1->alloc)) + return NULL; + + er = &box->shared_regs[reg1->idx]; + raw_spin_lock_irqsave(&er->lock, flags); + if (!atomic_read(&er->ref) || er->config1 == reg1->config) { + atomic_inc(&er->ref); + er->config1 = reg1->config; + ok = true; + } + raw_spin_unlock_irqrestore(&er->lock, flags); + + if (ok) { + if (box->phys_id >= 0) + reg1->alloc = 1; + return NULL; + } + return &constraint_empty; +} + +static void snbep_uncore_put_constraint(struct intel_uncore_box *box, + struct perf_event *event) +{ + struct intel_uncore_extra_reg *er; + struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; + + if (box->phys_id < 0 || !reg1->alloc) + return; + + er = &box->shared_regs[reg1->idx]; + atomic_dec(&er->ref); + reg1->alloc = 0; +} + +static int snbep_uncore_hw_config(struct intel_uncore_box *box, + struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct hw_perf_event_extra *reg1 = &hwc->extra_reg; @@ -234,16 +221,14 @@ static int snbep_uncore_hw_config(struct intel_uncore_box *box, struct perf_even SNBEP_CBO_MSR_OFFSET * box->pmu->pmu_idx; reg1->config = event->attr.config1 & SNBEP_CB0_MSR_PMON_BOX_FILTER_MASK; + } else if (box->pmu->type == &snbep_uncore_pcu) { + reg1->reg = SNBEP_PCU_MSR_PMON_BOX_FILTER; + reg1->config = event->attr.config1 & + SNBEP_PCU_MSR_PMON_BOX_FILTER_MASK; } else { - if (box->pmu->type == &snbep_uncore_pcu) { - reg1->reg = SNBEP_PCU_MSR_PMON_BOX_FILTER; - reg1->config = event->attr.config1 & SNBEP_PCU_MSR_PMON_BOX_FILTER_MASK; - } else { - return 0; - } + return 0; } reg1->idx = 0; - return 0; } @@ -287,19 +272,10 @@ static struct attribute *snbep_uncore_pcu_formats_attr[] = { &format_attr_thresh5.attr, &format_attr_occ_invert.attr, &format_attr_occ_edge.attr, - &format_attr_filter_band0.attr, - &format_attr_filter_band1.attr, - &format_attr_filter_band2.attr, - &format_attr_filter_band3.attr, - NULL, -}; - -static struct attribute *snbep_uncore_qpi_formats_attr[] = { - &format_attr_event_ext.attr, - &format_attr_umask.attr, - &format_attr_edge.attr, - &format_attr_inv.attr, - &format_attr_thresh8.attr, + &format_attr_filter_brand0.attr, + &format_attr_filter_brand1.attr, + &format_attr_filter_brand2.attr, + &format_attr_filter_brand3.attr, NULL, }; @@ -338,20 +314,15 @@ static struct attribute_group snbep_uncore_pcu_format_group = { .attrs = snbep_uncore_pcu_formats_attr, }; -static struct attribute_group snbep_uncore_qpi_format_group = { - .name = "format", - .attrs = snbep_uncore_qpi_formats_attr, -}; - static struct intel_uncore_ops snbep_uncore_msr_ops = { .init_box = snbep_uncore_msr_init_box, .disable_box = snbep_uncore_msr_disable_box, .enable_box = snbep_uncore_msr_enable_box, .disable_event = snbep_uncore_msr_disable_event, .enable_event = snbep_uncore_msr_enable_event, - .read_counter = uncore_msr_read_counter, - .get_constraint = uncore_get_constraint, - .put_constraint = uncore_put_constraint, + .read_counter = snbep_uncore_msr_read_counter, + .get_constraint = snbep_uncore_get_constraint, + .put_constraint = snbep_uncore_put_constraint, .hw_config = snbep_uncore_hw_config, }; @@ -514,13 +485,8 @@ static struct intel_uncore_type snbep_uncore_qpi = { .num_counters = 4, .num_boxes = 2, .perf_ctr_bits = 48, - .perf_ctr = SNBEP_PCI_PMON_CTR0, - .event_ctl = SNBEP_PCI_PMON_CTL0, - .event_mask = SNBEP_QPI_PCI_PMON_RAW_EVENT_MASK, - .box_ctl = SNBEP_PCI_PMON_BOX_CTL, - .ops = &snbep_uncore_pci_ops, .event_descs = snbep_uncore_qpi_events, - .format_group = &snbep_uncore_qpi_format_group, + SNBEP_UNCORE_PCI_COMMON_INIT(), }; @@ -621,1210 +587,190 @@ static void snbep_pci2phy_map_init(void) pci_read_config_dword(ubox_dev, 0x40, &config); nodeid = config; /* get the Node ID mapping */ - pci_read_config_dword(ubox_dev, 0x54, &config); - /* - * every three bits in the Node ID mapping register maps - * to a particular node. - */ - for (i = 0; i < 8; i++) { - if (nodeid == ((config >> (3 * i)) & 0x7)) { - pcibus_to_physid[bus] = i; - break; - } - } - }; - return; -} -/* end of Sandy Bridge-EP uncore support */ - -/* Sandy Bridge uncore support */ -static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - - if (hwc->idx < UNCORE_PMC_IDX_FIXED) - wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN); - else - wrmsrl(hwc->config_base, SNB_UNC_CTL_EN); -} - -static void snb_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event) -{ - wrmsrl(event->hw.config_base, 0); -} - -static void snb_uncore_msr_init_box(struct intel_uncore_box *box) -{ - if (box->pmu->pmu_idx == 0) { - wrmsrl(SNB_UNC_PERF_GLOBAL_CTL, - SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL); - } -} - -static struct attribute *snb_uncore_formats_attr[] = { - &format_attr_event.attr, - &format_attr_umask.attr, - &format_attr_edge.attr, - &format_attr_inv.attr, - &format_attr_cmask5.attr, - NULL, -}; - -static struct attribute_group snb_uncore_format_group = { - .name = "format", - .attrs = snb_uncore_formats_attr, -}; - -static struct intel_uncore_ops snb_uncore_msr_ops = { - .init_box = snb_uncore_msr_init_box, - .disable_event = snb_uncore_msr_disable_event, - .enable_event = snb_uncore_msr_enable_event, - .read_counter = uncore_msr_read_counter, -}; - -static struct event_constraint snb_uncore_cbox_constraints[] = { - UNCORE_EVENT_CONSTRAINT(0x80, 0x1), - UNCORE_EVENT_CONSTRAINT(0x83, 0x1), - EVENT_CONSTRAINT_END -}; - -static struct intel_uncore_type snb_uncore_cbox = { - .name = "cbox", - .num_counters = 2, - .num_boxes = 4, - .perf_ctr_bits = 44, - .fixed_ctr_bits = 48, - .perf_ctr = SNB_UNC_CBO_0_PER_CTR0, - .event_ctl = SNB_UNC_CBO_0_PERFEVTSEL0, - .fixed_ctr = SNB_UNC_FIXED_CTR, - .fixed_ctl = SNB_UNC_FIXED_CTR_CTRL, - .single_fixed = 1, - .event_mask = SNB_UNC_RAW_EVENT_MASK, - .msr_offset = SNB_UNC_CBO_MSR_OFFSET, - .constraints = snb_uncore_cbox_constraints, - .ops = &snb_uncore_msr_ops, - .format_group = &snb_uncore_format_group, -}; - -static struct intel_uncore_type *snb_msr_uncores[] = { - &snb_uncore_cbox, - NULL, -}; -/* end of Sandy Bridge uncore support */ - -/* Nehalem uncore support */ -static void nhm_uncore_msr_disable_box(struct intel_uncore_box *box) -{ - wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, 0); -} - -static void nhm_uncore_msr_enable_box(struct intel_uncore_box *box) -{ - wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, NHM_UNC_GLOBAL_CTL_EN_PC_ALL | NHM_UNC_GLOBAL_CTL_EN_FC); -} - -static void nhm_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - - if (hwc->idx < UNCORE_PMC_IDX_FIXED) - wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN); - else - wrmsrl(hwc->config_base, NHM_UNC_FIXED_CTR_CTL_EN); -} - -static struct attribute *nhm_uncore_formats_attr[] = { - &format_attr_event.attr, - &format_attr_umask.attr, - &format_attr_edge.attr, - &format_attr_inv.attr, - &format_attr_cmask8.attr, - NULL, -}; - -static struct attribute_group nhm_uncore_format_group = { - .name = "format", - .attrs = nhm_uncore_formats_attr, -}; - -static struct uncore_event_desc nhm_uncore_events[] = { - INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"), - INTEL_UNCORE_EVENT_DESC(qmc_writes_full_any, "event=0x2f,umask=0x0f"), - INTEL_UNCORE_EVENT_DESC(qmc_normal_reads_any, "event=0x2c,umask=0x0f"), - INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_reads, "event=0x20,umask=0x01"), - INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_writes, "event=0x20,umask=0x02"), - INTEL_UNCORE_EVENT_DESC(qhl_request_remote_reads, "event=0x20,umask=0x04"), - INTEL_UNCORE_EVENT_DESC(qhl_request_remote_writes, "event=0x20,umask=0x08"), - INTEL_UNCORE_EVENT_DESC(qhl_request_local_reads, "event=0x20,umask=0x10"), - INTEL_UNCORE_EVENT_DESC(qhl_request_local_writes, "event=0x20,umask=0x20"), - { /* end: all zeroes */ }, -}; - -static struct intel_uncore_ops nhm_uncore_msr_ops = { - .disable_box = nhm_uncore_msr_disable_box, - .enable_box = nhm_uncore_msr_enable_box, - .disable_event = snb_uncore_msr_disable_event, - .enable_event = nhm_uncore_msr_enable_event, - .read_counter = uncore_msr_read_counter, -}; - -static struct intel_uncore_type nhm_uncore = { - .name = "", - .num_counters = 8, - .num_boxes = 1, - .perf_ctr_bits = 48, - .fixed_ctr_bits = 48, - .event_ctl = NHM_UNC_PERFEVTSEL0, - .perf_ctr = NHM_UNC_UNCORE_PMC0, - .fixed_ctr = NHM_UNC_FIXED_CTR, - .fixed_ctl = NHM_UNC_FIXED_CTR_CTRL, - .event_mask = NHM_UNC_RAW_EVENT_MASK, - .event_descs = nhm_uncore_events, - .ops = &nhm_uncore_msr_ops, - .format_group = &nhm_uncore_format_group, -}; - -static struct intel_uncore_type *nhm_msr_uncores[] = { - &nhm_uncore, - NULL, -}; -/* end of Nehalem uncore support */ - -/* Nehalem-EX uncore support */ -#define __BITS_VALUE(x, i, n) ((typeof(x))(((x) >> ((i) * (n))) & \ - ((1ULL << (n)) - 1))) - -DEFINE_UNCORE_FORMAT_ATTR(event5, event, "config:1-5"); -DEFINE_UNCORE_FORMAT_ATTR(counter, counter, "config:6-7"); -DEFINE_UNCORE_FORMAT_ATTR(mm_cfg, mm_cfg, "config:63"); -DEFINE_UNCORE_FORMAT_ATTR(match, match, "config1:0-63"); -DEFINE_UNCORE_FORMAT_ATTR(mask, mask, "config2:0-63"); - -static void nhmex_uncore_msr_init_box(struct intel_uncore_box *box) -{ - wrmsrl(NHMEX_U_MSR_PMON_GLOBAL_CTL, NHMEX_U_PMON_GLOBAL_EN_ALL); -} - -static void nhmex_uncore_msr_disable_box(struct intel_uncore_box *box) -{ - unsigned msr = uncore_msr_box_ctl(box); - u64 config; - - if (msr) { - rdmsrl(msr, config); - config &= ~((1ULL << uncore_num_counters(box)) - 1); - /* WBox has a fixed counter */ - if (uncore_msr_fixed_ctl(box)) - config &= ~NHMEX_W_PMON_GLOBAL_FIXED_EN; - wrmsrl(msr, config); - } -} - -static void nhmex_uncore_msr_enable_box(struct intel_uncore_box *box) -{ - unsigned msr = uncore_msr_box_ctl(box); - u64 config; - - if (msr) { - rdmsrl(msr, config); - config |= (1ULL << uncore_num_counters(box)) - 1; - /* WBox has a fixed counter */ - if (uncore_msr_fixed_ctl(box)) - config |= NHMEX_W_PMON_GLOBAL_FIXED_EN; - wrmsrl(msr, config); - } -} - -static void nhmex_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event) -{ - wrmsrl(event->hw.config_base, 0); -} - -static void nhmex_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - - if (hwc->idx >= UNCORE_PMC_IDX_FIXED) - wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0); - else if (box->pmu->type->event_mask & NHMEX_PMON_CTL_EN_BIT0) - wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT22); - else - wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0); -} - -#define NHMEX_UNCORE_OPS_COMMON_INIT() \ - .init_box = nhmex_uncore_msr_init_box, \ - .disable_box = nhmex_uncore_msr_disable_box, \ - .enable_box = nhmex_uncore_msr_enable_box, \ - .disable_event = nhmex_uncore_msr_disable_event, \ - .read_counter = uncore_msr_read_counter - -static struct intel_uncore_ops nhmex_uncore_ops = { - NHMEX_UNCORE_OPS_COMMON_INIT(), - .enable_event = nhmex_uncore_msr_enable_event, -}; - -static struct attribute *nhmex_uncore_ubox_formats_attr[] = { - &format_attr_event.attr, - &format_attr_edge.attr, - NULL, -}; - -static struct attribute_group nhmex_uncore_ubox_format_group = { - .name = "format", - .attrs = nhmex_uncore_ubox_formats_attr, -}; - -static struct intel_uncore_type nhmex_uncore_ubox = { - .name = "ubox", - .num_counters = 1, - .num_boxes = 1, - .perf_ctr_bits = 48, - .event_ctl = NHMEX_U_MSR_PMON_EV_SEL, - .perf_ctr = NHMEX_U_MSR_PMON_CTR, - .event_mask = NHMEX_U_PMON_RAW_EVENT_MASK, - .box_ctl = NHMEX_U_MSR_PMON_GLOBAL_CTL, - .ops = &nhmex_uncore_ops, - .format_group = &nhmex_uncore_ubox_format_group -}; - -static struct attribute *nhmex_uncore_cbox_formats_attr[] = { - &format_attr_event.attr, - &format_attr_umask.attr, - &format_attr_edge.attr, - &format_attr_inv.attr, - &format_attr_thresh8.attr, - NULL, -}; - -static struct attribute_group nhmex_uncore_cbox_format_group = { - .name = "format", - .attrs = nhmex_uncore_cbox_formats_attr, -}; - -static struct intel_uncore_type nhmex_uncore_cbox = { - .name = "cbox", - .num_counters = 6, - .num_boxes = 8, - .perf_ctr_bits = 48, - .event_ctl = NHMEX_C0_MSR_PMON_EV_SEL0, - .perf_ctr = NHMEX_C0_MSR_PMON_CTR0, - .event_mask = NHMEX_PMON_RAW_EVENT_MASK, - .box_ctl = NHMEX_C0_MSR_PMON_GLOBAL_CTL, - .msr_offset = NHMEX_C_MSR_OFFSET, - .pair_ctr_ctl = 1, - .ops = &nhmex_uncore_ops, - .format_group = &nhmex_uncore_cbox_format_group -}; - -static struct uncore_event_desc nhmex_uncore_wbox_events[] = { - INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0"), - { /* end: all zeroes */ }, -}; - -static struct intel_uncore_type nhmex_uncore_wbox = { - .name = "wbox", - .num_counters = 4, - .num_boxes = 1, - .perf_ctr_bits = 48, - .event_ctl = NHMEX_W_MSR_PMON_CNT0, - .perf_ctr = NHMEX_W_MSR_PMON_EVT_SEL0, - .fixed_ctr = NHMEX_W_MSR_PMON_FIXED_CTR, - .fixed_ctl = NHMEX_W_MSR_PMON_FIXED_CTL, - .event_mask = NHMEX_PMON_RAW_EVENT_MASK, - .box_ctl = NHMEX_W_MSR_GLOBAL_CTL, - .pair_ctr_ctl = 1, - .event_descs = nhmex_uncore_wbox_events, - .ops = &nhmex_uncore_ops, - .format_group = &nhmex_uncore_cbox_format_group -}; - -static int nhmex_bbox_hw_config(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &hwc->extra_reg; - struct hw_perf_event_extra *reg2 = &hwc->branch_reg; - int ctr, ev_sel; - - ctr = (hwc->config & NHMEX_B_PMON_CTR_MASK) >> - NHMEX_B_PMON_CTR_SHIFT; - ev_sel = (hwc->config & NHMEX_B_PMON_CTL_EV_SEL_MASK) >> - NHMEX_B_PMON_CTL_EV_SEL_SHIFT; - - /* events that do not use the match/mask registers */ - if ((ctr == 0 && ev_sel > 0x3) || (ctr == 1 && ev_sel > 0x6) || - (ctr == 2 && ev_sel != 0x4) || ctr == 3) - return 0; - - if (box->pmu->pmu_idx == 0) - reg1->reg = NHMEX_B0_MSR_MATCH; - else - reg1->reg = NHMEX_B1_MSR_MATCH; - reg1->idx = 0; - reg1->config = event->attr.config1; - reg2->config = event->attr.config2; - return 0; -} - -static void nhmex_bbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &hwc->extra_reg; - struct hw_perf_event_extra *reg2 = &hwc->branch_reg; - - if (reg1->idx != EXTRA_REG_NONE) { - wrmsrl(reg1->reg, reg1->config); - wrmsrl(reg1->reg + 1, reg2->config); - } - wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0 | - (hwc->config & NHMEX_B_PMON_CTL_EV_SEL_MASK)); -} - -/* - * The Bbox has 4 counters, but each counter monitors different events. - * Use bits 6-7 in the event config to select counter. - */ -static struct event_constraint nhmex_uncore_bbox_constraints[] = { - EVENT_CONSTRAINT(0 , 1, 0xc0), - EVENT_CONSTRAINT(0x40, 2, 0xc0), - EVENT_CONSTRAINT(0x80, 4, 0xc0), - EVENT_CONSTRAINT(0xc0, 8, 0xc0), - EVENT_CONSTRAINT_END, -}; - -static struct attribute *nhmex_uncore_bbox_formats_attr[] = { - &format_attr_event5.attr, - &format_attr_counter.attr, - &format_attr_match.attr, - &format_attr_mask.attr, - NULL, -}; - -static struct attribute_group nhmex_uncore_bbox_format_group = { - .name = "format", - .attrs = nhmex_uncore_bbox_formats_attr, -}; - -static struct intel_uncore_ops nhmex_uncore_bbox_ops = { - NHMEX_UNCORE_OPS_COMMON_INIT(), - .enable_event = nhmex_bbox_msr_enable_event, - .hw_config = nhmex_bbox_hw_config, - .get_constraint = uncore_get_constraint, - .put_constraint = uncore_put_constraint, -}; - -static struct intel_uncore_type nhmex_uncore_bbox = { - .name = "bbox", - .num_counters = 4, - .num_boxes = 2, - .perf_ctr_bits = 48, - .event_ctl = NHMEX_B0_MSR_PMON_CTL0, - .perf_ctr = NHMEX_B0_MSR_PMON_CTR0, - .event_mask = NHMEX_B_PMON_RAW_EVENT_MASK, - .box_ctl = NHMEX_B0_MSR_PMON_GLOBAL_CTL, - .msr_offset = NHMEX_B_MSR_OFFSET, - .pair_ctr_ctl = 1, - .num_shared_regs = 1, - .constraints = nhmex_uncore_bbox_constraints, - .ops = &nhmex_uncore_bbox_ops, - .format_group = &nhmex_uncore_bbox_format_group -}; - -static int nhmex_sbox_hw_config(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; - struct hw_perf_event_extra *reg2 = &event->hw.branch_reg; - - if (event->attr.config & NHMEX_S_PMON_MM_CFG_EN) { - reg1->config = event->attr.config1; - reg2->config = event->attr.config2; - } else { - reg1->config = ~0ULL; - reg2->config = ~0ULL; - } - - if (box->pmu->pmu_idx == 0) - reg1->reg = NHMEX_S0_MSR_MM_CFG; - else - reg1->reg = NHMEX_S1_MSR_MM_CFG; - - reg1->idx = 0; - - return 0; -} - -static void nhmex_sbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &hwc->extra_reg; - struct hw_perf_event_extra *reg2 = &hwc->branch_reg; - - wrmsrl(reg1->reg, 0); - if (reg1->config != ~0ULL || reg2->config != ~0ULL) { - wrmsrl(reg1->reg + 1, reg1->config); - wrmsrl(reg1->reg + 2, reg2->config); - wrmsrl(reg1->reg, NHMEX_S_PMON_MM_CFG_EN); - } - wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT22); -} - -static struct attribute *nhmex_uncore_sbox_formats_attr[] = { - &format_attr_event.attr, - &format_attr_umask.attr, - &format_attr_edge.attr, - &format_attr_inv.attr, - &format_attr_thresh8.attr, - &format_attr_mm_cfg.attr, - &format_attr_match.attr, - &format_attr_mask.attr, - NULL, -}; - -static struct attribute_group nhmex_uncore_sbox_format_group = { - .name = "format", - .attrs = nhmex_uncore_sbox_formats_attr, -}; - -static struct intel_uncore_ops nhmex_uncore_sbox_ops = { - NHMEX_UNCORE_OPS_COMMON_INIT(), - .enable_event = nhmex_sbox_msr_enable_event, - .hw_config = nhmex_sbox_hw_config, - .get_constraint = uncore_get_constraint, - .put_constraint = uncore_put_constraint, -}; - -static struct intel_uncore_type nhmex_uncore_sbox = { - .name = "sbox", - .num_counters = 4, - .num_boxes = 2, - .perf_ctr_bits = 48, - .event_ctl = NHMEX_S0_MSR_PMON_CTL0, - .perf_ctr = NHMEX_S0_MSR_PMON_CTR0, - .event_mask = NHMEX_PMON_RAW_EVENT_MASK, - .box_ctl = NHMEX_S0_MSR_PMON_GLOBAL_CTL, - .msr_offset = NHMEX_S_MSR_OFFSET, - .pair_ctr_ctl = 1, - .num_shared_regs = 1, - .ops = &nhmex_uncore_sbox_ops, - .format_group = &nhmex_uncore_sbox_format_group -}; - -enum { - EXTRA_REG_NHMEX_M_FILTER, - EXTRA_REG_NHMEX_M_DSP, - EXTRA_REG_NHMEX_M_ISS, - EXTRA_REG_NHMEX_M_MAP, - EXTRA_REG_NHMEX_M_MSC_THR, - EXTRA_REG_NHMEX_M_PGT, - EXTRA_REG_NHMEX_M_PLD, - EXTRA_REG_NHMEX_M_ZDP_CTL_FVC, -}; - -static struct extra_reg nhmex_uncore_mbox_extra_regs[] = { - MBOX_INC_SEL_EXTAR_REG(0x0, DSP), - MBOX_INC_SEL_EXTAR_REG(0x4, MSC_THR), - MBOX_INC_SEL_EXTAR_REG(0x5, MSC_THR), - MBOX_INC_SEL_EXTAR_REG(0x9, ISS), - /* event 0xa uses two extra registers */ - MBOX_INC_SEL_EXTAR_REG(0xa, ISS), - MBOX_INC_SEL_EXTAR_REG(0xa, PLD), - MBOX_INC_SEL_EXTAR_REG(0xb, PLD), - /* events 0xd ~ 0x10 use the same extra register */ - MBOX_INC_SEL_EXTAR_REG(0xd, ZDP_CTL_FVC), - MBOX_INC_SEL_EXTAR_REG(0xe, ZDP_CTL_FVC), - MBOX_INC_SEL_EXTAR_REG(0xf, ZDP_CTL_FVC), - MBOX_INC_SEL_EXTAR_REG(0x10, ZDP_CTL_FVC), - MBOX_INC_SEL_EXTAR_REG(0x16, PGT), - MBOX_SET_FLAG_SEL_EXTRA_REG(0x0, DSP), - MBOX_SET_FLAG_SEL_EXTRA_REG(0x1, ISS), - MBOX_SET_FLAG_SEL_EXTRA_REG(0x5, PGT), - MBOX_SET_FLAG_SEL_EXTRA_REG(0x6, MAP), - EVENT_EXTRA_END -}; - -static bool nhmex_mbox_get_shared_reg(struct intel_uncore_box *box, int idx, u64 config) -{ - struct intel_uncore_extra_reg *er; - unsigned long flags; - bool ret = false; - u64 mask; - - if (idx < EXTRA_REG_NHMEX_M_ZDP_CTL_FVC) { - er = &box->shared_regs[idx]; - raw_spin_lock_irqsave(&er->lock, flags); - if (!atomic_read(&er->ref) || er->config == config) { - atomic_inc(&er->ref); - er->config = config; - ret = true; - } - raw_spin_unlock_irqrestore(&er->lock, flags); - - return ret; - } - /* - * The ZDP_CTL_FVC MSR has 4 fields which are used to control - * events 0xd ~ 0x10. Besides these 4 fields, there are additional - * fields which are shared. - */ - idx -= EXTRA_REG_NHMEX_M_ZDP_CTL_FVC; - if (WARN_ON_ONCE(idx >= 4)) - return false; - - /* mask of the shared fields */ - mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK; - er = &box->shared_regs[EXTRA_REG_NHMEX_M_ZDP_CTL_FVC]; - - raw_spin_lock_irqsave(&er->lock, flags); - /* add mask of the non-shared field if it's in use */ - if (__BITS_VALUE(atomic_read(&er->ref), idx, 8)) - mask |= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx); - - if (!atomic_read(&er->ref) || !((er->config ^ config) & mask)) { - atomic_add(1 << (idx * 8), &er->ref); - mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK | - NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx); - er->config &= ~mask; - er->config |= (config & mask); - ret = true; - } - raw_spin_unlock_irqrestore(&er->lock, flags); - - return ret; -} - -static void nhmex_mbox_put_shared_reg(struct intel_uncore_box *box, int idx) -{ - struct intel_uncore_extra_reg *er; - - if (idx < EXTRA_REG_NHMEX_M_ZDP_CTL_FVC) { - er = &box->shared_regs[idx]; - atomic_dec(&er->ref); - return; - } - - idx -= EXTRA_REG_NHMEX_M_ZDP_CTL_FVC; - er = &box->shared_regs[EXTRA_REG_NHMEX_M_ZDP_CTL_FVC]; - atomic_sub(1 << (idx * 8), &er->ref); -} - -u64 nhmex_mbox_alter_er(struct perf_event *event, int new_idx, bool modify) -{ - struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &hwc->extra_reg; - int idx, orig_idx = __BITS_VALUE(reg1->idx, 0, 8); - u64 config = reg1->config; - - /* get the non-shared control bits and shift them */ - idx = orig_idx - EXTRA_REG_NHMEX_M_ZDP_CTL_FVC; - config &= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx); - if (new_idx > orig_idx) { - idx = new_idx - orig_idx; - config <<= 3 * idx; - } else { - idx = orig_idx - new_idx; - config >>= 3 * idx; - } - - /* add the shared control bits back */ - config |= NHMEX_M_PMON_ZDP_CTL_FVC_MASK & reg1->config; - if (modify) { - /* adjust the main event selector */ - if (new_idx > orig_idx) - hwc->config += idx << NHMEX_M_PMON_CTL_INC_SEL_SHIFT; - else - hwc->config -= idx << NHMEX_M_PMON_CTL_INC_SEL_SHIFT; - reg1->config = config; - reg1->idx = ~0xff | new_idx; - } - return config; -} - -static struct event_constraint * -nhmex_mbox_get_constraint(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; - struct hw_perf_event_extra *reg2 = &event->hw.branch_reg; - int i, idx[2], alloc = 0; - u64 config1 = reg1->config; - - idx[0] = __BITS_VALUE(reg1->idx, 0, 8); - idx[1] = __BITS_VALUE(reg1->idx, 1, 8); -again: - for (i = 0; i < 2; i++) { - if (!uncore_box_is_fake(box) && (reg1->alloc & (0x1 << i))) - idx[i] = 0xff; - - if (idx[i] == 0xff) - continue; - - if (!nhmex_mbox_get_shared_reg(box, idx[i], - __BITS_VALUE(config1, i, 32))) - goto fail; - alloc |= (0x1 << i); - } - - /* for the match/mask registers */ - if ((uncore_box_is_fake(box) || !reg2->alloc) && - !nhmex_mbox_get_shared_reg(box, reg2->idx, reg2->config)) - goto fail; - - /* - * If it's a fake box -- as per validate_{group,event}() we - * shouldn't touch event state and we can avoid doing so - * since both will only call get_event_constraints() once - * on each event, this avoids the need for reg->alloc. - */ - if (!uncore_box_is_fake(box)) { - if (idx[0] != 0xff && idx[0] != __BITS_VALUE(reg1->idx, 0, 8)) - nhmex_mbox_alter_er(event, idx[0], true); - reg1->alloc |= alloc; - reg2->alloc = 1; - } - return NULL; -fail: - if (idx[0] != 0xff && !(alloc & 0x1) && - idx[0] >= EXTRA_REG_NHMEX_M_ZDP_CTL_FVC) { + pci_read_config_dword(ubox_dev, 0x54, &config); /* - * events 0xd ~ 0x10 are functional identical, but are - * controlled by different fields in the ZDP_CTL_FVC - * register. If we failed to take one field, try the - * rest 3 choices. + * every three bits in the Node ID mapping register maps + * to a particular node. */ - BUG_ON(__BITS_VALUE(reg1->idx, 1, 8) != 0xff); - idx[0] -= EXTRA_REG_NHMEX_M_ZDP_CTL_FVC; - idx[0] = (idx[0] + 1) % 4; - idx[0] += EXTRA_REG_NHMEX_M_ZDP_CTL_FVC; - if (idx[0] != __BITS_VALUE(reg1->idx, 0, 8)) { - config1 = nhmex_mbox_alter_er(event, idx[0], false); - goto again; + for (i = 0; i < 8; i++) { + if (nodeid == ((config >> (3 * i)) & 0x7)) { + pcibus_to_physid[bus] = i; + break; + } } - } - - if (alloc & 0x1) - nhmex_mbox_put_shared_reg(box, idx[0]); - if (alloc & 0x2) - nhmex_mbox_put_shared_reg(box, idx[1]); - return &constraint_empty; -} - -static void nhmex_mbox_put_constraint(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; - struct hw_perf_event_extra *reg2 = &event->hw.branch_reg; - - if (uncore_box_is_fake(box)) - return; - - if (reg1->alloc & 0x1) - nhmex_mbox_put_shared_reg(box, __BITS_VALUE(reg1->idx, 0, 8)); - if (reg1->alloc & 0x2) - nhmex_mbox_put_shared_reg(box, __BITS_VALUE(reg1->idx, 1, 8)); - reg1->alloc = 0; - - if (reg2->alloc) { - nhmex_mbox_put_shared_reg(box, reg2->idx); - reg2->alloc = 0; - } + }; + return; } +/* end of Sandy Bridge-EP uncore support */ -static int nhmex_mbox_extra_reg_idx(struct extra_reg *er) -{ - if (er->idx < EXTRA_REG_NHMEX_M_ZDP_CTL_FVC) - return er->idx; - return er->idx + (er->event >> NHMEX_M_PMON_CTL_INC_SEL_SHIFT) - 0xd; -} -static int nhmex_mbox_hw_config(struct intel_uncore_box *box, struct perf_event *event) +/* Sandy Bridge uncore support */ +static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, + struct perf_event *event) { - struct intel_uncore_type *type = box->pmu->type; - struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; - struct hw_perf_event_extra *reg2 = &event->hw.branch_reg; - struct extra_reg *er; - unsigned msr; - int reg_idx = 0; - - if (WARN_ON_ONCE(reg1->idx != -1)) - return -EINVAL; - /* - * The mbox events may require 2 extra MSRs at the most. But only - * the lower 32 bits in these MSRs are significant, so we can use - * config1 to pass two MSRs' config. - */ - for (er = nhmex_uncore_mbox_extra_regs; er->msr; er++) { - if (er->event != (event->hw.config & er->config_mask)) - continue; - if (event->attr.config1 & ~er->valid_mask) - return -EINVAL; - if (er->idx == __BITS_VALUE(reg1->idx, 0, 8) || - er->idx == __BITS_VALUE(reg1->idx, 1, 8)) - continue; - if (WARN_ON_ONCE(reg_idx >= 2)) - return -EINVAL; - - msr = er->msr + type->msr_offset * box->pmu->pmu_idx; - if (WARN_ON_ONCE(msr >= 0xffff || er->idx >= 0xff)) - return -EINVAL; - - /* always use the 32~63 bits to pass the PLD config */ - if (er->idx == EXTRA_REG_NHMEX_M_PLD) - reg_idx = 1; + struct hw_perf_event *hwc = &event->hw; - reg1->idx &= ~(0xff << (reg_idx * 8)); - reg1->reg &= ~(0xffff << (reg_idx * 16)); - reg1->idx |= nhmex_mbox_extra_reg_idx(er) << (reg_idx * 8); - reg1->reg |= msr << (reg_idx * 16); - reg1->config = event->attr.config1; - reg_idx++; - } - /* use config2 to pass the filter config */ - reg2->idx = EXTRA_REG_NHMEX_M_FILTER; - if (event->attr.config2 & NHMEX_M_PMON_MM_CFG_EN) - reg2->config = event->attr.config2; - else - reg2->config = ~0ULL; - if (box->pmu->pmu_idx == 0) - reg2->reg = NHMEX_M0_MSR_PMU_MM_CFG; + if (hwc->idx < UNCORE_PMC_IDX_FIXED) + wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN); else - reg2->reg = NHMEX_M1_MSR_PMU_MM_CFG; - - return 0; + wrmsrl(hwc->config_base, SNB_UNC_CTL_EN); } -static u64 nhmex_mbox_shared_reg_config(struct intel_uncore_box *box, int idx) +static void snb_uncore_msr_disable_event(struct intel_uncore_box *box, + struct perf_event *event) { - struct intel_uncore_extra_reg *er; - unsigned long flags; - u64 config; - - if (idx < EXTRA_REG_NHMEX_M_ZDP_CTL_FVC) - return box->shared_regs[idx].config; + wrmsrl(event->hw.config_base, 0); +} - er = &box->shared_regs[EXTRA_REG_NHMEX_M_ZDP_CTL_FVC]; - raw_spin_lock_irqsave(&er->lock, flags); - config = er->config; - raw_spin_unlock_irqrestore(&er->lock, flags); - return config; +static u64 snb_uncore_msr_read_counter(struct intel_uncore_box *box, + struct perf_event *event) +{ + u64 count; + rdmsrl(event->hw.event_base, count); + return count; } -static void nhmex_mbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) +static void snb_uncore_msr_init_box(struct intel_uncore_box *box) { - struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &hwc->extra_reg; - struct hw_perf_event_extra *reg2 = &hwc->branch_reg; - int idx; - - idx = __BITS_VALUE(reg1->idx, 0, 8); - if (idx != 0xff) - wrmsrl(__BITS_VALUE(reg1->reg, 0, 16), - nhmex_mbox_shared_reg_config(box, idx)); - idx = __BITS_VALUE(reg1->idx, 1, 8); - if (idx != 0xff) - wrmsrl(__BITS_VALUE(reg1->reg, 1, 16), - nhmex_mbox_shared_reg_config(box, idx)); - - wrmsrl(reg2->reg, 0); - if (reg2->config != ~0ULL) { - wrmsrl(reg2->reg + 1, - reg2->config & NHMEX_M_PMON_ADDR_MATCH_MASK); - wrmsrl(reg2->reg + 2, NHMEX_M_PMON_ADDR_MASK_MASK & - (reg2->config >> NHMEX_M_PMON_ADDR_MASK_SHIFT)); - wrmsrl(reg2->reg, NHMEX_M_PMON_MM_CFG_EN); + if (box->pmu->pmu_idx == 0) { + wrmsrl(SNB_UNC_PERF_GLOBAL_CTL, + SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL); } +} - wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0); -} - -DEFINE_UNCORE_FORMAT_ATTR(count_mode, count_mode, "config:2-3"); -DEFINE_UNCORE_FORMAT_ATTR(storage_mode, storage_mode, "config:4-5"); -DEFINE_UNCORE_FORMAT_ATTR(wrap_mode, wrap_mode, "config:6"); -DEFINE_UNCORE_FORMAT_ATTR(flag_mode, flag_mode, "config:7"); -DEFINE_UNCORE_FORMAT_ATTR(inc_sel, inc_sel, "config:9-13"); -DEFINE_UNCORE_FORMAT_ATTR(set_flag_sel, set_flag_sel, "config:19-21"); -DEFINE_UNCORE_FORMAT_ATTR(filter_cfg, filter_cfg, "config2:63"); -DEFINE_UNCORE_FORMAT_ATTR(filter_match, filter_match, "config2:0-33"); -DEFINE_UNCORE_FORMAT_ATTR(filter_mask, filter_mask, "config2:34-61"); -DEFINE_UNCORE_FORMAT_ATTR(dsp, dsp, "config1:0-31"); -DEFINE_UNCORE_FORMAT_ATTR(thr, thr, "config1:0-31"); -DEFINE_UNCORE_FORMAT_ATTR(fvc, fvc, "config1:0-31"); -DEFINE_UNCORE_FORMAT_ATTR(pgt, pgt, "config1:0-31"); -DEFINE_UNCORE_FORMAT_ATTR(map, map, "config1:0-31"); -DEFINE_UNCORE_FORMAT_ATTR(iss, iss, "config1:0-31"); -DEFINE_UNCORE_FORMAT_ATTR(pld, pld, "config1:32-63"); - -static struct attribute *nhmex_uncore_mbox_formats_attr[] = { - &format_attr_count_mode.attr, - &format_attr_storage_mode.attr, - &format_attr_wrap_mode.attr, - &format_attr_flag_mode.attr, - &format_attr_inc_sel.attr, - &format_attr_set_flag_sel.attr, - &format_attr_filter_cfg.attr, - &format_attr_filter_match.attr, - &format_attr_filter_mask.attr, - &format_attr_dsp.attr, - &format_attr_thr.attr, - &format_attr_fvc.attr, - &format_attr_pgt.attr, - &format_attr_map.attr, - &format_attr_iss.attr, - &format_attr_pld.attr, +static struct attribute *snb_uncore_formats_attr[] = { + &format_attr_event.attr, + &format_attr_umask.attr, + &format_attr_edge.attr, + &format_attr_inv.attr, + &format_attr_cmask5.attr, NULL, }; -static struct attribute_group nhmex_uncore_mbox_format_group = { - .name = "format", - .attrs = nhmex_uncore_mbox_formats_attr, +static struct attribute_group snb_uncore_format_group = { + .name = "format", + .attrs = snb_uncore_formats_attr, }; -static struct uncore_event_desc nhmex_uncore_mbox_events[] = { - INTEL_UNCORE_EVENT_DESC(bbox_cmds_read, "inc_sel=0xd,fvc=0x2800"), - INTEL_UNCORE_EVENT_DESC(bbox_cmds_write, "inc_sel=0xd,fvc=0x2820"), - { /* end: all zeroes */ }, +static struct intel_uncore_ops snb_uncore_msr_ops = { + .init_box = snb_uncore_msr_init_box, + .disable_event = snb_uncore_msr_disable_event, + .enable_event = snb_uncore_msr_enable_event, + .read_counter = snb_uncore_msr_read_counter, }; -static struct intel_uncore_ops nhmex_uncore_mbox_ops = { - NHMEX_UNCORE_OPS_COMMON_INIT(), - .enable_event = nhmex_mbox_msr_enable_event, - .hw_config = nhmex_mbox_hw_config, - .get_constraint = nhmex_mbox_get_constraint, - .put_constraint = nhmex_mbox_put_constraint, +static struct event_constraint snb_uncore_cbox_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x80, 0x1), + UNCORE_EVENT_CONSTRAINT(0x83, 0x1), + EVENT_CONSTRAINT_END }; -static struct intel_uncore_type nhmex_uncore_mbox = { - .name = "mbox", - .num_counters = 6, - .num_boxes = 2, - .perf_ctr_bits = 48, - .event_ctl = NHMEX_M0_MSR_PMU_CTL0, - .perf_ctr = NHMEX_M0_MSR_PMU_CNT0, - .event_mask = NHMEX_M_PMON_RAW_EVENT_MASK, - .box_ctl = NHMEX_M0_MSR_GLOBAL_CTL, - .msr_offset = NHMEX_M_MSR_OFFSET, - .pair_ctr_ctl = 1, - .num_shared_regs = 8, - .event_descs = nhmex_uncore_mbox_events, - .ops = &nhmex_uncore_mbox_ops, - .format_group = &nhmex_uncore_mbox_format_group, +static struct intel_uncore_type snb_uncore_cbox = { + .name = "cbox", + .num_counters = 2, + .num_boxes = 4, + .perf_ctr_bits = 44, + .fixed_ctr_bits = 48, + .perf_ctr = SNB_UNC_CBO_0_PER_CTR0, + .event_ctl = SNB_UNC_CBO_0_PERFEVTSEL0, + .fixed_ctr = SNB_UNC_FIXED_CTR, + .fixed_ctl = SNB_UNC_FIXED_CTR_CTRL, + .single_fixed = 1, + .event_mask = SNB_UNC_RAW_EVENT_MASK, + .msr_offset = SNB_UNC_CBO_MSR_OFFSET, + .constraints = snb_uncore_cbox_constraints, + .ops = &snb_uncore_msr_ops, + .format_group = &snb_uncore_format_group, }; -void nhmex_rbox_alter_er(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &hwc->extra_reg; - int port; - - /* adjust the main event selector */ - if (reg1->idx % 2) { - reg1->idx--; - hwc->config -= 1 << NHMEX_R_PMON_CTL_EV_SEL_SHIFT; - } else { - reg1->idx++; - hwc->config += 1 << NHMEX_R_PMON_CTL_EV_SEL_SHIFT; - } - - /* adjust address or config of extra register */ - port = reg1->idx / 6 + box->pmu->pmu_idx * 4; - switch (reg1->idx % 6) { - case 0: - reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG0(port); - break; - case 1: - reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG1(port); - break; - case 2: - /* the 8~15 bits to the 0~7 bits */ - reg1->config >>= 8; - break; - case 3: - /* the 0~7 bits to the 8~15 bits */ - reg1->config <<= 8; - break; - case 4: - reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port); - break; - case 5: - reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port); - break; - }; -} - -/* - * Each rbox has 4 event set which monitor PQI port 0~3 or 4~7. - * An event set consists of 6 events, the 3rd and 4th events in - * an event set use the same extra register. So an event set uses - * 5 extra registers. - */ -static struct event_constraint * -nhmex_rbox_get_constraint(struct intel_uncore_box *box, struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &hwc->extra_reg; - struct hw_perf_event_extra *reg2 = &hwc->branch_reg; - struct intel_uncore_extra_reg *er; - unsigned long flags; - int idx, er_idx; - u64 config1; - bool ok = false; - - if (!uncore_box_is_fake(box) && reg1->alloc) - return NULL; - - idx = reg1->idx % 6; - config1 = reg1->config; -again: - er_idx = idx; - /* the 3rd and 4th events use the same extra register */ - if (er_idx > 2) - er_idx--; - er_idx += (reg1->idx / 6) * 5; - - er = &box->shared_regs[er_idx]; - raw_spin_lock_irqsave(&er->lock, flags); - if (idx < 2) { - if (!atomic_read(&er->ref) || er->config == reg1->config) { - atomic_inc(&er->ref); - er->config = reg1->config; - ok = true; - } - } else if (idx == 2 || idx == 3) { - /* - * these two events use different fields in a extra register, - * the 0~7 bits and the 8~15 bits respectively. - */ - u64 mask = 0xff << ((idx - 2) * 8); - if (!__BITS_VALUE(atomic_read(&er->ref), idx - 2, 8) || - !((er->config ^ config1) & mask)) { - atomic_add(1 << ((idx - 2) * 8), &er->ref); - er->config &= ~mask; - er->config |= config1 & mask; - ok = true; - } - } else { - if (!atomic_read(&er->ref) || - (er->config == (hwc->config >> 32) && - er->config1 == reg1->config && - er->config2 == reg2->config)) { - atomic_inc(&er->ref); - er->config = (hwc->config >> 32); - er->config1 = reg1->config; - er->config2 = reg2->config; - ok = true; - } - } - raw_spin_unlock_irqrestore(&er->lock, flags); - - if (!ok) { - /* - * The Rbox events are always in pairs. The paired - * events are functional identical, but use different - * extra registers. If we failed to take an extra - * register, try the alternative. - */ - if (idx % 2) - idx--; - else - idx++; - if (idx != reg1->idx % 6) { - if (idx == 2) - config1 >>= 8; - else if (idx == 3) - config1 <<= 8; - goto again; - } - } else { - if (!uncore_box_is_fake(box)) { - if (idx != reg1->idx % 6) - nhmex_rbox_alter_er(box, event); - reg1->alloc = 1; - } - return NULL; - } - return &constraint_empty; -} - -static void nhmex_rbox_put_constraint(struct intel_uncore_box *box, struct perf_event *event) -{ - struct intel_uncore_extra_reg *er; - struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; - int idx, er_idx; - - if (uncore_box_is_fake(box) || !reg1->alloc) - return; - - idx = reg1->idx % 6; - er_idx = idx; - if (er_idx > 2) - er_idx--; - er_idx += (reg1->idx / 6) * 5; - - er = &box->shared_regs[er_idx]; - if (idx == 2 || idx == 3) - atomic_sub(1 << ((idx - 2) * 8), &er->ref); - else - atomic_dec(&er->ref); - - reg1->alloc = 0; -} +static struct intel_uncore_type *snb_msr_uncores[] = { + &snb_uncore_cbox, + NULL, +}; +/* end of Sandy Bridge uncore support */ -static int nhmex_rbox_hw_config(struct intel_uncore_box *box, struct perf_event *event) +/* Nehalem uncore support */ +static void nhm_uncore_msr_disable_box(struct intel_uncore_box *box) { - struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; - struct hw_perf_event_extra *reg2 = &event->hw.branch_reg; - int port, idx; - - idx = (event->hw.config & NHMEX_R_PMON_CTL_EV_SEL_MASK) >> - NHMEX_R_PMON_CTL_EV_SEL_SHIFT; - if (idx >= 0x18) - return -EINVAL; - - reg1->idx = idx; - reg1->config = event->attr.config1; - - port = idx / 6 + box->pmu->pmu_idx * 4; - idx %= 6; - switch (idx) { - case 0: - reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG0(port); - break; - case 1: - reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG1(port); - break; - case 2: - case 3: - reg1->reg = NHMEX_R_MSR_PORTN_QLX_CFG(port); - break; - case 4: - case 5: - if (idx == 4) - reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port); - else - reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port); - reg2->config = event->attr.config2; - hwc->config |= event->attr.config & (~0ULL << 32); - break; - }; - return 0; + wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, 0); } -static u64 nhmex_rbox_shared_reg_config(struct intel_uncore_box *box, int idx) +static void nhm_uncore_msr_enable_box(struct intel_uncore_box *box) { - struct intel_uncore_extra_reg *er; - unsigned long flags; - u64 config; - - er = &box->shared_regs[idx]; - - raw_spin_lock_irqsave(&er->lock, flags); - config = er->config; - raw_spin_unlock_irqrestore(&er->lock, flags); - - return config; + wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, + NHM_UNC_GLOBAL_CTL_EN_PC_ALL | NHM_UNC_GLOBAL_CTL_EN_FC); } -static void nhmex_rbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) +static void nhm_uncore_msr_enable_event(struct intel_uncore_box *box, + struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - struct hw_perf_event_extra *reg1 = &hwc->extra_reg; - struct hw_perf_event_extra *reg2 = &hwc->branch_reg; - int idx, er_idx; - - idx = reg1->idx % 6; - er_idx = idx; - if (er_idx > 2) - er_idx--; - er_idx += (reg1->idx / 6) * 5; - - switch (idx) { - case 0: - case 1: - wrmsrl(reg1->reg, reg1->config); - break; - case 2: - case 3: - wrmsrl(reg1->reg, nhmex_rbox_shared_reg_config(box, er_idx)); - break; - case 4: - case 5: - wrmsrl(reg1->reg, reg1->config); - wrmsrl(reg1->reg + 1, hwc->config >> 32); - wrmsrl(reg1->reg + 2, reg2->config); - break; - }; - wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0 | - (hwc->config & NHMEX_R_PMON_CTL_EV_SEL_MASK)); + if (hwc->idx < UNCORE_PMC_IDX_FIXED) + wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN); + else + wrmsrl(hwc->config_base, NHM_UNC_FIXED_CTR_CTL_EN); } -DEFINE_UNCORE_FORMAT_ATTR(xbr_match, xbr_match, "config:32-63"); -DEFINE_UNCORE_FORMAT_ATTR(xbr_mm_cfg, xbr_mm_cfg, "config1:0-63"); -DEFINE_UNCORE_FORMAT_ATTR(xbr_mask, xbr_mask, "config2:0-63"); -DEFINE_UNCORE_FORMAT_ATTR(qlx_cfg, qlx_cfg, "config1:0-15"); -DEFINE_UNCORE_FORMAT_ATTR(iperf_cfg, iperf_cfg, "config1:0-31"); - -static struct attribute *nhmex_uncore_rbox_formats_attr[] = { - &format_attr_event5.attr, - &format_attr_xbr_mm_cfg.attr, - &format_attr_xbr_match.attr, - &format_attr_xbr_mask.attr, - &format_attr_qlx_cfg.attr, - &format_attr_iperf_cfg.attr, +static struct attribute *nhm_uncore_formats_attr[] = { + &format_attr_event.attr, + &format_attr_umask.attr, + &format_attr_edge.attr, + &format_attr_inv.attr, + &format_attr_cmask8.attr, NULL, }; -static struct attribute_group nhmex_uncore_rbox_format_group = { +static struct attribute_group nhm_uncore_format_group = { .name = "format", - .attrs = nhmex_uncore_rbox_formats_attr, + .attrs = nhm_uncore_formats_attr, }; -static struct uncore_event_desc nhmex_uncore_rbox_events[] = { - INTEL_UNCORE_EVENT_DESC(qpi0_flit_send, "event=0x0,iperf_cfg=0x80000000"), - INTEL_UNCORE_EVENT_DESC(qpi1_filt_send, "event=0x6,iperf_cfg=0x80000000"), - INTEL_UNCORE_EVENT_DESC(qpi0_idle_filt, "event=0x0,iperf_cfg=0x40000000"), - INTEL_UNCORE_EVENT_DESC(qpi1_idle_filt, "event=0x6,iperf_cfg=0x40000000"), - INTEL_UNCORE_EVENT_DESC(qpi0_date_response, "event=0x0,iperf_cfg=0xc4"), - INTEL_UNCORE_EVENT_DESC(qpi1_date_response, "event=0x6,iperf_cfg=0xc4"), +static struct uncore_event_desc nhm_uncore_events[] = { + INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"), + INTEL_UNCORE_EVENT_DESC(qmc_writes_full_any, "event=0x2f,umask=0x0f"), + INTEL_UNCORE_EVENT_DESC(qmc_normal_reads_any, "event=0x2c,umask=0x0f"), + INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_reads, "event=0x20,umask=0x01"), + INTEL_UNCORE_EVENT_DESC(qhl_request_ioh_writes, "event=0x20,umask=0x02"), + INTEL_UNCORE_EVENT_DESC(qhl_request_remote_reads, "event=0x20,umask=0x04"), + INTEL_UNCORE_EVENT_DESC(qhl_request_remote_writes, "event=0x20,umask=0x08"), + INTEL_UNCORE_EVENT_DESC(qhl_request_local_reads, "event=0x20,umask=0x10"), + INTEL_UNCORE_EVENT_DESC(qhl_request_local_writes, "event=0x20,umask=0x20"), { /* end: all zeroes */ }, }; -static struct intel_uncore_ops nhmex_uncore_rbox_ops = { - NHMEX_UNCORE_OPS_COMMON_INIT(), - .enable_event = nhmex_rbox_msr_enable_event, - .hw_config = nhmex_rbox_hw_config, - .get_constraint = nhmex_rbox_get_constraint, - .put_constraint = nhmex_rbox_put_constraint, +static struct intel_uncore_ops nhm_uncore_msr_ops = { + .disable_box = nhm_uncore_msr_disable_box, + .enable_box = nhm_uncore_msr_enable_box, + .disable_event = snb_uncore_msr_disable_event, + .enable_event = nhm_uncore_msr_enable_event, + .read_counter = snb_uncore_msr_read_counter, }; -static struct intel_uncore_type nhmex_uncore_rbox = { - .name = "rbox", - .num_counters = 8, - .num_boxes = 2, - .perf_ctr_bits = 48, - .event_ctl = NHMEX_R_MSR_PMON_CTL0, - .perf_ctr = NHMEX_R_MSR_PMON_CNT0, - .event_mask = NHMEX_R_PMON_RAW_EVENT_MASK, - .box_ctl = NHMEX_R_MSR_GLOBAL_CTL, - .msr_offset = NHMEX_R_MSR_OFFSET, - .pair_ctr_ctl = 1, - .num_shared_regs = 20, - .event_descs = nhmex_uncore_rbox_events, - .ops = &nhmex_uncore_rbox_ops, - .format_group = &nhmex_uncore_rbox_format_group +static struct intel_uncore_type nhm_uncore = { + .name = "", + .num_counters = 8, + .num_boxes = 1, + .perf_ctr_bits = 48, + .fixed_ctr_bits = 48, + .event_ctl = NHM_UNC_PERFEVTSEL0, + .perf_ctr = NHM_UNC_UNCORE_PMC0, + .fixed_ctr = NHM_UNC_FIXED_CTR, + .fixed_ctl = NHM_UNC_FIXED_CTR_CTRL, + .event_mask = NHM_UNC_RAW_EVENT_MASK, + .event_descs = nhm_uncore_events, + .ops = &nhm_uncore_msr_ops, + .format_group = &nhm_uncore_format_group, }; -static struct intel_uncore_type *nhmex_msr_uncores[] = { - &nhmex_uncore_ubox, - &nhmex_uncore_cbox, - &nhmex_uncore_bbox, - &nhmex_uncore_sbox, - &nhmex_uncore_mbox, - &nhmex_uncore_rbox, - &nhmex_uncore_wbox, +static struct intel_uncore_type *nhm_msr_uncores[] = { + &nhm_uncore, NULL, }; -/* end of Nehalem-EX uncore support */ +/* end of Nehalem uncore support */ -static void uncore_assign_hw_event(struct intel_uncore_box *box, struct perf_event *event, int idx) +static void uncore_assign_hw_event(struct intel_uncore_box *box, + struct perf_event *event, int idx) { struct hw_perf_event *hwc = &event->hw; @@ -1841,7 +787,8 @@ static void uncore_assign_hw_event(struct intel_uncore_box *box, struct perf_eve hwc->event_base = uncore_perf_ctr(box, hwc->idx); } -static void uncore_perf_event_update(struct intel_uncore_box *box, struct perf_event *event) +static void uncore_perf_event_update(struct intel_uncore_box *box, + struct perf_event *event) { u64 prev_count, new_count, delta; int shift; @@ -1911,12 +858,14 @@ static void uncore_pmu_init_hrtimer(struct intel_uncore_box *box) box->hrtimer.function = uncore_pmu_hrtimer; } -struct intel_uncore_box *uncore_alloc_box(struct intel_uncore_type *type, int cpu) +struct intel_uncore_box *uncore_alloc_box(struct intel_uncore_type *type, + int cpu) { struct intel_uncore_box *box; int i, size; - size = sizeof(*box) + type->num_shared_regs * sizeof(struct intel_uncore_extra_reg); + size = sizeof(*box) + type->num_shared_regs * + sizeof(struct intel_uncore_extra_reg); box = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, cpu_to_node(cpu)); if (!box) @@ -1966,11 +915,12 @@ static struct intel_uncore_box *uncore_event_to_box(struct perf_event *event) * perf core schedules event on the basis of cpu, uncore events are * collected by one of the cpus inside a physical package. */ - return uncore_pmu_to_box(uncore_event_to_pmu(event), smp_processor_id()); + return uncore_pmu_to_box(uncore_event_to_pmu(event), + smp_processor_id()); } -static int -uncore_collect_events(struct intel_uncore_box *box, struct perf_event *leader, bool dogrp) +static int uncore_collect_events(struct intel_uncore_box *box, + struct perf_event *leader, bool dogrp) { struct perf_event *event; int n, max_count; @@ -2002,7 +952,8 @@ uncore_collect_events(struct intel_uncore_box *box, struct perf_event *leader, b } static struct event_constraint * -uncore_get_event_constraint(struct intel_uncore_box *box, struct perf_event *event) +uncore_get_event_constraint(struct intel_uncore_box *box, + struct perf_event *event) { struct intel_uncore_type *type = box->pmu->type; struct event_constraint *c; @@ -2026,13 +977,15 @@ uncore_get_event_constraint(struct intel_uncore_box *box, struct perf_event *eve return &type->unconstrainted; } -static void uncore_put_event_constraint(struct intel_uncore_box *box, struct perf_event *event) +static void uncore_put_event_constraint(struct intel_uncore_box *box, + struct perf_event *event) { if (box->pmu->type->ops->put_constraint) box->pmu->type->ops->put_constraint(box, event); } -static int uncore_assign_events(struct intel_uncore_box *box, int assign[], int n) +static int uncore_assign_events(struct intel_uncore_box *box, + int assign[], int n) { unsigned long used_mask[BITS_TO_LONGS(UNCORE_PMC_IDX_MAX)]; struct event_constraint *c, *constraints[UNCORE_PMC_IDX_MAX]; @@ -2454,7 +1407,8 @@ static bool pcidrv_registered; /* * add a pci uncore device */ -static int __devinit uncore_pci_add(struct intel_uncore_type *type, struct pci_dev *pdev) +static int __devinit uncore_pci_add(struct intel_uncore_type *type, + struct pci_dev *pdev) { struct intel_uncore_pmu *pmu; struct intel_uncore_box *box; @@ -2531,7 +1485,6 @@ static int __devinit uncore_pci_probe(struct pci_dev *pdev, struct intel_uncore_type *type; type = (struct intel_uncore_type *)id->driver_data; - return uncore_pci_add(type, pdev); } @@ -2659,8 +1612,8 @@ static int __cpuinit uncore_cpu_prepare(int cpu, int phys_id) return 0; } -static void __cpuinit -uncore_change_context(struct intel_uncore_type **uncores, int old_cpu, int new_cpu) +static void __cpuinit uncore_change_context(struct intel_uncore_type **uncores, + int old_cpu, int new_cpu) { struct intel_uncore_type *type; struct intel_uncore_pmu *pmu; @@ -2741,8 +1694,8 @@ static void __cpuinit uncore_event_init_cpu(int cpu) uncore_change_context(pci_uncores, -1, cpu); } -static int - __cpuinit uncore_cpu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) +static int __cpuinit uncore_cpu_notifier(struct notifier_block *self, + unsigned long action, void *hcpu) { unsigned int cpu = (long)hcpu; @@ -2779,12 +1732,12 @@ static int } static struct notifier_block uncore_cpu_nb __cpuinitdata = { - .notifier_call = uncore_cpu_notifier, + .notifier_call = uncore_cpu_notifier, /* * to migrate uncore events, our notifier should be executed * before perf core's notifier. */ - .priority = CPU_PRI_PERF + 1, + .priority = CPU_PRI_PERF + 1, }; static void __init uncore_cpu_setup(void *dummy) @@ -2814,9 +1767,6 @@ static int __init uncore_cpu_init(void) snbep_uncore_cbox.num_boxes = max_cores; msr_uncores = snbep_msr_uncores; break; - case 46: - msr_uncores = nhmex_msr_uncores; - break; default: return 0; } diff --git a/trunk/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/trunk/arch/x86/kernel/cpu/perf_event_intel_uncore.h index f3851892e077..b13e9ea81def 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event_intel_uncore.h +++ b/trunk/arch/x86/kernel/cpu/perf_event_intel_uncore.h @@ -5,6 +5,8 @@ #include "perf_event.h" #define UNCORE_PMU_NAME_LEN 32 +#define UNCORE_BOX_HASH_SIZE 8 + #define UNCORE_PMU_HRTIMER_INTERVAL (60 * NSEC_PER_SEC) #define UNCORE_FIXED_EVENT 0xff @@ -113,10 +115,6 @@ SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \ SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET) -#define SNBEP_QPI_PCI_PMON_RAW_EVENT_MASK \ - (SNBEP_PMON_RAW_EVENT_MASK | \ - SNBEP_PMON_CTL_EV_SEL_EXT) - /* SNB-EP pci control register */ #define SNBEP_PCI_PMON_BOX_CTL 0xf4 #define SNBEP_PCI_PMON_CTL0 0xd8 @@ -160,193 +158,6 @@ #define SNBEP_PCU_MSR_CORE_C3_CTR 0x3fc #define SNBEP_PCU_MSR_CORE_C6_CTR 0x3fd -/* NHM-EX event control */ -#define NHMEX_PMON_CTL_EV_SEL_MASK 0x000000ff -#define NHMEX_PMON_CTL_UMASK_MASK 0x0000ff00 -#define NHMEX_PMON_CTL_EN_BIT0 (1 << 0) -#define NHMEX_PMON_CTL_EDGE_DET (1 << 18) -#define NHMEX_PMON_CTL_PMI_EN (1 << 20) -#define NHMEX_PMON_CTL_EN_BIT22 (1 << 22) -#define NHMEX_PMON_CTL_INVERT (1 << 23) -#define NHMEX_PMON_CTL_TRESH_MASK 0xff000000 -#define NHMEX_PMON_RAW_EVENT_MASK (NHMEX_PMON_CTL_EV_SEL_MASK | \ - NHMEX_PMON_CTL_UMASK_MASK | \ - NHMEX_PMON_CTL_EDGE_DET | \ - NHMEX_PMON_CTL_INVERT | \ - NHMEX_PMON_CTL_TRESH_MASK) - -/* NHM-EX Ubox */ -#define NHMEX_U_MSR_PMON_GLOBAL_CTL 0xc00 -#define NHMEX_U_MSR_PMON_CTR 0xc11 -#define NHMEX_U_MSR_PMON_EV_SEL 0xc10 - -#define NHMEX_U_PMON_GLOBAL_EN (1 << 0) -#define NHMEX_U_PMON_GLOBAL_PMI_CORE_SEL 0x0000001e -#define NHMEX_U_PMON_GLOBAL_EN_ALL (1 << 28) -#define NHMEX_U_PMON_GLOBAL_RST_ALL (1 << 29) -#define NHMEX_U_PMON_GLOBAL_FRZ_ALL (1 << 31) - -#define NHMEX_U_PMON_RAW_EVENT_MASK \ - (NHMEX_PMON_CTL_EV_SEL_MASK | \ - NHMEX_PMON_CTL_EDGE_DET) - -/* NHM-EX Cbox */ -#define NHMEX_C0_MSR_PMON_GLOBAL_CTL 0xd00 -#define NHMEX_C0_MSR_PMON_CTR0 0xd11 -#define NHMEX_C0_MSR_PMON_EV_SEL0 0xd10 -#define NHMEX_C_MSR_OFFSET 0x20 - -/* NHM-EX Bbox */ -#define NHMEX_B0_MSR_PMON_GLOBAL_CTL 0xc20 -#define NHMEX_B0_MSR_PMON_CTR0 0xc31 -#define NHMEX_B0_MSR_PMON_CTL0 0xc30 -#define NHMEX_B_MSR_OFFSET 0x40 -#define NHMEX_B0_MSR_MATCH 0xe45 -#define NHMEX_B0_MSR_MASK 0xe46 -#define NHMEX_B1_MSR_MATCH 0xe4d -#define NHMEX_B1_MSR_MASK 0xe4e - -#define NHMEX_B_PMON_CTL_EN (1 << 0) -#define NHMEX_B_PMON_CTL_EV_SEL_SHIFT 1 -#define NHMEX_B_PMON_CTL_EV_SEL_MASK \ - (0x1f << NHMEX_B_PMON_CTL_EV_SEL_SHIFT) -#define NHMEX_B_PMON_CTR_SHIFT 6 -#define NHMEX_B_PMON_CTR_MASK \ - (0x3 << NHMEX_B_PMON_CTR_SHIFT) -#define NHMEX_B_PMON_RAW_EVENT_MASK \ - (NHMEX_B_PMON_CTL_EV_SEL_MASK | \ - NHMEX_B_PMON_CTR_MASK) - -/* NHM-EX Sbox */ -#define NHMEX_S0_MSR_PMON_GLOBAL_CTL 0xc40 -#define NHMEX_S0_MSR_PMON_CTR0 0xc51 -#define NHMEX_S0_MSR_PMON_CTL0 0xc50 -#define NHMEX_S_MSR_OFFSET 0x80 -#define NHMEX_S0_MSR_MM_CFG 0xe48 -#define NHMEX_S0_MSR_MATCH 0xe49 -#define NHMEX_S0_MSR_MASK 0xe4a -#define NHMEX_S1_MSR_MM_CFG 0xe58 -#define NHMEX_S1_MSR_MATCH 0xe59 -#define NHMEX_S1_MSR_MASK 0xe5a - -#define NHMEX_S_PMON_MM_CFG_EN (0x1ULL << 63) - -/* NHM-EX Mbox */ -#define NHMEX_M0_MSR_GLOBAL_CTL 0xca0 -#define NHMEX_M0_MSR_PMU_DSP 0xca5 -#define NHMEX_M0_MSR_PMU_ISS 0xca6 -#define NHMEX_M0_MSR_PMU_MAP 0xca7 -#define NHMEX_M0_MSR_PMU_MSC_THR 0xca8 -#define NHMEX_M0_MSR_PMU_PGT 0xca9 -#define NHMEX_M0_MSR_PMU_PLD 0xcaa -#define NHMEX_M0_MSR_PMU_ZDP_CTL_FVC 0xcab -#define NHMEX_M0_MSR_PMU_CTL0 0xcb0 -#define NHMEX_M0_MSR_PMU_CNT0 0xcb1 -#define NHMEX_M_MSR_OFFSET 0x40 -#define NHMEX_M0_MSR_PMU_MM_CFG 0xe54 -#define NHMEX_M1_MSR_PMU_MM_CFG 0xe5c - -#define NHMEX_M_PMON_MM_CFG_EN (1ULL << 63) -#define NHMEX_M_PMON_ADDR_MATCH_MASK 0x3ffffffffULL -#define NHMEX_M_PMON_ADDR_MASK_MASK 0x7ffffffULL -#define NHMEX_M_PMON_ADDR_MASK_SHIFT 34 - -#define NHMEX_M_PMON_CTL_EN (1 << 0) -#define NHMEX_M_PMON_CTL_PMI_EN (1 << 1) -#define NHMEX_M_PMON_CTL_COUNT_MODE_SHIFT 2 -#define NHMEX_M_PMON_CTL_COUNT_MODE_MASK \ - (0x3 << NHMEX_M_PMON_CTL_COUNT_MODE_SHIFT) -#define NHMEX_M_PMON_CTL_STORAGE_MODE_SHIFT 4 -#define NHMEX_M_PMON_CTL_STORAGE_MODE_MASK \ - (0x3 << NHMEX_M_PMON_CTL_STORAGE_MODE_SHIFT) -#define NHMEX_M_PMON_CTL_WRAP_MODE (1 << 6) -#define NHMEX_M_PMON_CTL_FLAG_MODE (1 << 7) -#define NHMEX_M_PMON_CTL_INC_SEL_SHIFT 9 -#define NHMEX_M_PMON_CTL_INC_SEL_MASK \ - (0x1f << NHMEX_M_PMON_CTL_INC_SEL_SHIFT) -#define NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT 19 -#define NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK \ - (0x7 << NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT) -#define NHMEX_M_PMON_RAW_EVENT_MASK \ - (NHMEX_M_PMON_CTL_COUNT_MODE_MASK | \ - NHMEX_M_PMON_CTL_STORAGE_MODE_MASK | \ - NHMEX_M_PMON_CTL_WRAP_MODE | \ - NHMEX_M_PMON_CTL_FLAG_MODE | \ - NHMEX_M_PMON_CTL_INC_SEL_MASK | \ - NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK) - - -#define NHMEX_M_PMON_ZDP_CTL_FVC_FVID_MASK 0x1f -#define NHMEX_M_PMON_ZDP_CTL_FVC_BCMD_MASK (0x7 << 5) -#define NHMEX_M_PMON_ZDP_CTL_FVC_RSP_MASK (0x7 << 8) -#define NHMEX_M_PMON_ZDP_CTL_FVC_PBOX_INIT_ERR (1 << 23) -#define NHMEX_M_PMON_ZDP_CTL_FVC_MASK \ - (NHMEX_M_PMON_ZDP_CTL_FVC_FVID_MASK | \ - NHMEX_M_PMON_ZDP_CTL_FVC_BCMD_MASK | \ - NHMEX_M_PMON_ZDP_CTL_FVC_RSP_MASK | \ - NHMEX_M_PMON_ZDP_CTL_FVC_PBOX_INIT_ERR) -#define NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(n) (0x7 << (11 + 3 * (n))) - -/* - * use the 9~13 bits to select event If the 7th bit is not set, - * otherwise use the 19~21 bits to select event. - */ -#define MBOX_INC_SEL(x) ((x) << NHMEX_M_PMON_CTL_INC_SEL_SHIFT) -#define MBOX_SET_FLAG_SEL(x) (((x) << NHMEX_M_PMON_CTL_SET_FLAG_SEL_SHIFT) | \ - NHMEX_M_PMON_CTL_FLAG_MODE) -#define MBOX_INC_SEL_MASK (NHMEX_M_PMON_CTL_INC_SEL_MASK | \ - NHMEX_M_PMON_CTL_FLAG_MODE) -#define MBOX_SET_FLAG_SEL_MASK (NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK | \ - NHMEX_M_PMON_CTL_FLAG_MODE) -#define MBOX_INC_SEL_EXTAR_REG(c, r) \ - EVENT_EXTRA_REG(MBOX_INC_SEL(c), NHMEX_M0_MSR_PMU_##r, \ - MBOX_INC_SEL_MASK, (u64)-1, NHMEX_M_##r) -#define MBOX_SET_FLAG_SEL_EXTRA_REG(c, r) \ - EVENT_EXTRA_REG(MBOX_SET_FLAG_SEL(c), NHMEX_M0_MSR_PMU_##r, \ - MBOX_SET_FLAG_SEL_MASK, \ - (u64)-1, NHMEX_M_##r) - -/* NHM-EX Rbox */ -#define NHMEX_R_MSR_GLOBAL_CTL 0xe00 -#define NHMEX_R_MSR_PMON_CTL0 0xe10 -#define NHMEX_R_MSR_PMON_CNT0 0xe11 -#define NHMEX_R_MSR_OFFSET 0x20 - -#define NHMEX_R_MSR_PORTN_QLX_CFG(n) \ - ((n) < 4 ? (0xe0c + (n)) : (0xe2c + (n) - 4)) -#define NHMEX_R_MSR_PORTN_IPERF_CFG0(n) (0xe04 + (n)) -#define NHMEX_R_MSR_PORTN_IPERF_CFG1(n) (0xe24 + (n)) -#define NHMEX_R_MSR_PORTN_XBR_OFFSET(n) \ - (((n) < 4 ? 0 : 0x10) + (n) * 4) -#define NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n) \ - (0xe60 + NHMEX_R_MSR_PORTN_XBR_OFFSET(n)) -#define NHMEX_R_MSR_PORTN_XBR_SET1_MATCH(n) \ - (NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n) + 1) -#define NHMEX_R_MSR_PORTN_XBR_SET1_MASK(n) \ - (NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(n) + 2) -#define NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n) \ - (0xe70 + NHMEX_R_MSR_PORTN_XBR_OFFSET(n)) -#define NHMEX_R_MSR_PORTN_XBR_SET2_MATCH(n) \ - (NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n) + 1) -#define NHMEX_R_MSR_PORTN_XBR_SET2_MASK(n) \ - (NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(n) + 2) - -#define NHMEX_R_PMON_CTL_EN (1 << 0) -#define NHMEX_R_PMON_CTL_EV_SEL_SHIFT 1 -#define NHMEX_R_PMON_CTL_EV_SEL_MASK \ - (0x1f << NHMEX_R_PMON_CTL_EV_SEL_SHIFT) -#define NHMEX_R_PMON_CTL_PMI_EN (1 << 6) -#define NHMEX_R_PMON_RAW_EVENT_MASK NHMEX_R_PMON_CTL_EV_SEL_MASK - -/* NHM-EX Wbox */ -#define NHMEX_W_MSR_GLOBAL_CTL 0xc80 -#define NHMEX_W_MSR_PMON_CNT0 0xc90 -#define NHMEX_W_MSR_PMON_EVT_SEL0 0xc91 -#define NHMEX_W_MSR_PMON_FIXED_CTR 0x394 -#define NHMEX_W_MSR_PMON_FIXED_CTL 0x395 - -#define NHMEX_W_PMON_GLOBAL_FIXED_EN (1ULL << 31) - struct intel_uncore_ops; struct intel_uncore_pmu; struct intel_uncore_box; @@ -367,7 +178,6 @@ struct intel_uncore_type { unsigned msr_offset; unsigned num_shared_regs:8; unsigned single_fixed:1; - unsigned pair_ctr_ctl:1; struct event_constraint unconstrainted; struct event_constraint *constraints; struct intel_uncore_pmu *pmus; @@ -403,7 +213,7 @@ struct intel_uncore_pmu { struct intel_uncore_extra_reg { raw_spinlock_t lock; - u64 config, config1, config2; + u64 config1; atomic_t ref; }; @@ -513,16 +323,14 @@ unsigned uncore_msr_fixed_ctr(struct intel_uncore_box *box) static inline unsigned uncore_msr_event_ctl(struct intel_uncore_box *box, int idx) { - return box->pmu->type->event_ctl + - (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) + + return idx + box->pmu->type->event_ctl + box->pmu->type->msr_offset * box->pmu->pmu_idx; } static inline unsigned uncore_msr_perf_ctr(struct intel_uncore_box *box, int idx) { - return box->pmu->type->perf_ctr + - (box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) + + return idx + box->pmu->type->perf_ctr + box->pmu->type->msr_offset * box->pmu->pmu_idx; } @@ -614,8 +422,3 @@ static inline void uncore_box_init(struct intel_uncore_box *box) box->pmu->type->ops->init_box(box); } } - -static inline bool uncore_box_is_fake(struct intel_uncore_box *box) -{ - return (box->phys_id < 0); -} diff --git a/trunk/arch/x86/um/asm/ptrace.h b/trunk/arch/x86/um/asm/ptrace.h index e72cd0df5ba3..950dfb7b8417 100644 --- a/trunk/arch/x86/um/asm/ptrace.h +++ b/trunk/arch/x86/um/asm/ptrace.h @@ -30,10 +30,10 @@ #define profile_pc(regs) PT_REGS_IP(regs) #define UPT_RESTART_SYSCALL(r) (UPT_IP(r) -= 2) -#define PT_REGS_SET_SYSCALL_RETURN(r, res) (PT_REGS_AX(r) = (res)) +#define UPT_SET_SYSCALL_RETURN(r, res) (UPT_AX(r) = (res)) -static inline long regs_return_value(struct pt_regs *regs) +static inline long regs_return_value(struct uml_pt_regs *regs) { - return PT_REGS_AX(regs); + return UPT_AX(regs); } #endif /* __UM_X86_PTRACE_H */ diff --git a/trunk/arch/xtensa/Kconfig b/trunk/arch/xtensa/Kconfig index 8ed64cfae4ff..8a3f8351f438 100644 --- a/trunk/arch/xtensa/Kconfig +++ b/trunk/arch/xtensa/Kconfig @@ -7,7 +7,6 @@ config ZONE_DMA config XTENSA def_bool y select HAVE_IDE - select GENERIC_ATOMIC64 select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES diff --git a/trunk/block/blk-cgroup.c b/trunk/block/blk-cgroup.c index f3b44a65fc7a..e7dee617358e 100644 --- a/trunk/block/blk-cgroup.c +++ b/trunk/block/blk-cgroup.c @@ -31,6 +31,27 @@ EXPORT_SYMBOL_GPL(blkcg_root); static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS]; +struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) +{ + return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id), + struct blkcg, css); +} +EXPORT_SYMBOL_GPL(cgroup_to_blkcg); + +static struct blkcg *task_blkcg(struct task_struct *tsk) +{ + return container_of(task_subsys_state(tsk, blkio_subsys_id), + struct blkcg, css); +} + +struct blkcg *bio_blkcg(struct bio *bio) +{ + if (bio && bio->bi_css) + return container_of(bio->bi_css, struct blkcg, css); + return task_blkcg(current); +} +EXPORT_SYMBOL_GPL(bio_blkcg); + static bool blkcg_policy_enabled(struct request_queue *q, const struct blkcg_policy *pol) { @@ -63,7 +84,6 @@ static void blkg_free(struct blkcg_gq *blkg) kfree(pd); } - blk_exit_rl(&blkg->rl); kfree(blkg); } @@ -71,18 +91,16 @@ static void blkg_free(struct blkcg_gq *blkg) * blkg_alloc - allocate a blkg * @blkcg: block cgroup the new blkg is associated with * @q: request_queue the new blkg is associated with - * @gfp_mask: allocation mask to use * * Allocate a new blkg assocating @blkcg and @q. */ -static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, - gfp_t gfp_mask) +static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q) { struct blkcg_gq *blkg; int i; /* alloc and init base part */ - blkg = kzalloc_node(sizeof(*blkg), gfp_mask, q->node); + blkg = kzalloc_node(sizeof(*blkg), GFP_ATOMIC, q->node); if (!blkg) return NULL; @@ -91,13 +109,6 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, blkg->blkcg = blkcg; blkg->refcnt = 1; - /* root blkg uses @q->root_rl, init rl only for !root blkgs */ - if (blkcg != &blkcg_root) { - if (blk_init_rl(&blkg->rl, q, gfp_mask)) - goto err_free; - blkg->rl.blkg = blkg; - } - for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkcg_policy *pol = blkcg_policy[i]; struct blkg_policy_data *pd; @@ -106,9 +117,11 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, continue; /* alloc per-policy data and attach it to blkg */ - pd = kzalloc_node(pol->pd_size, gfp_mask, q->node); - if (!pd) - goto err_free; + pd = kzalloc_node(pol->pd_size, GFP_ATOMIC, q->node); + if (!pd) { + blkg_free(blkg); + return NULL; + } blkg->pd[i] = pd; pd->blkg = blkg; @@ -119,10 +132,6 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, } return blkg; - -err_free: - blkg_free(blkg); - return NULL; } static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, @@ -166,13 +175,9 @@ struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q) } EXPORT_SYMBOL_GPL(blkg_lookup); -/* - * If @new_blkg is %NULL, this function tries to allocate a new one as - * necessary using %GFP_ATOMIC. @new_blkg is always consumed on return. - */ static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg, - struct request_queue *q, - struct blkcg_gq *new_blkg) + struct request_queue *q) + __releases(q->queue_lock) __acquires(q->queue_lock) { struct blkcg_gq *blkg; int ret; @@ -184,26 +189,24 @@ static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg, blkg = __blkg_lookup(blkcg, q); if (blkg) { rcu_assign_pointer(blkcg->blkg_hint, blkg); - goto out_free; + return blkg; } /* blkg holds a reference to blkcg */ - if (!css_tryget(&blkcg->css)) { - blkg = ERR_PTR(-EINVAL); - goto out_free; - } + if (!css_tryget(&blkcg->css)) + return ERR_PTR(-EINVAL); /* allocate */ - if (!new_blkg) { - new_blkg = blkg_alloc(blkcg, q, GFP_ATOMIC); - if (unlikely(!new_blkg)) { - blkg = ERR_PTR(-ENOMEM); - goto out_put; - } - } - blkg = new_blkg; + ret = -ENOMEM; + blkg = blkg_alloc(blkcg, q); + if (unlikely(!blkg)) + goto err_put; /* insert */ + ret = radix_tree_preload(GFP_ATOMIC); + if (ret) + goto err_free; + spin_lock(&blkcg->lock); ret = radix_tree_insert(&blkcg->blkg_tree, q->id, blkg); if (likely(!ret)) { @@ -212,15 +215,15 @@ static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg, } spin_unlock(&blkcg->lock); + radix_tree_preload_end(); + if (!ret) return blkg; - - blkg = ERR_PTR(ret); -out_put: +err_free: + blkg_free(blkg); +err_put: css_put(&blkcg->css); -out_free: - blkg_free(new_blkg); - return blkg; + return ERR_PTR(ret); } struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, @@ -232,7 +235,7 @@ struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, */ if (unlikely(blk_queue_bypass(q))) return ERR_PTR(blk_queue_dead(q) ? -EINVAL : -EBUSY); - return __blkg_lookup_create(blkcg, q, NULL); + return __blkg_lookup_create(blkcg, q); } EXPORT_SYMBOL_GPL(blkg_lookup_create); @@ -310,38 +313,6 @@ void __blkg_release(struct blkcg_gq *blkg) } EXPORT_SYMBOL_GPL(__blkg_release); -/* - * The next function used by blk_queue_for_each_rl(). It's a bit tricky - * because the root blkg uses @q->root_rl instead of its own rl. - */ -struct request_list *__blk_queue_next_rl(struct request_list *rl, - struct request_queue *q) -{ - struct list_head *ent; - struct blkcg_gq *blkg; - - /* - * Determine the current blkg list_head. The first entry is - * root_rl which is off @q->blkg_list and mapped to the head. - */ - if (rl == &q->root_rl) { - ent = &q->blkg_list; - } else { - blkg = container_of(rl, struct blkcg_gq, rl); - ent = &blkg->q_node; - } - - /* walk to the next list_head, skip root blkcg */ - ent = ent->next; - if (ent == &q->root_blkg->q_node) - ent = ent->next; - if (ent == &q->blkg_list) - return NULL; - - blkg = container_of(ent, struct blkcg_gq, q_node); - return &blkg->rl; -} - static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) { @@ -763,36 +734,24 @@ int blkcg_activate_policy(struct request_queue *q, struct blkcg_gq *blkg; struct blkg_policy_data *pd, *n; int cnt = 0, ret; - bool preloaded; if (blkcg_policy_enabled(q, pol)) return 0; - /* preallocations for root blkg */ - blkg = blkg_alloc(&blkcg_root, q, GFP_KERNEL); - if (!blkg) - return -ENOMEM; - - preloaded = !radix_tree_preload(GFP_KERNEL); - blk_queue_bypass_start(q); /* make sure the root blkg exists and count the existing blkgs */ spin_lock_irq(q->queue_lock); rcu_read_lock(); - blkg = __blkg_lookup_create(&blkcg_root, q, blkg); + blkg = __blkg_lookup_create(&blkcg_root, q); rcu_read_unlock(); - if (preloaded) - radix_tree_preload_end(); - if (IS_ERR(blkg)) { ret = PTR_ERR(blkg); goto out_unlock; } q->root_blkg = blkg; - q->root_rl.blkg = blkg; list_for_each_entry(blkg, &q->blkg_list, q_node) cnt++; diff --git a/trunk/block/blk-cgroup.h b/trunk/block/blk-cgroup.h index 24597309e23d..8ac457ce7783 100644 --- a/trunk/block/blk-cgroup.h +++ b/trunk/block/blk-cgroup.h @@ -17,7 +17,6 @@ #include #include #include -#include /* Max limits for throttle policy */ #define THROTL_IOPS_MAX UINT_MAX @@ -94,8 +93,6 @@ struct blkcg_gq { struct list_head q_node; struct hlist_node blkcg_node; struct blkcg *blkcg; - /* request allocation list for this blkcg-q pair */ - struct request_list rl; /* reference count */ int refcnt; @@ -123,6 +120,8 @@ struct blkcg_policy { extern struct blkcg blkcg_root; +struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup); +struct blkcg *bio_blkcg(struct bio *bio); struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q); struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, struct request_queue *q); @@ -161,25 +160,6 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, void blkg_conf_finish(struct blkg_conf_ctx *ctx); -static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) -{ - return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id), - struct blkcg, css); -} - -static inline struct blkcg *task_blkcg(struct task_struct *tsk) -{ - return container_of(task_subsys_state(tsk, blkio_subsys_id), - struct blkcg, css); -} - -static inline struct blkcg *bio_blkcg(struct bio *bio) -{ - if (bio && bio->bi_css) - return container_of(bio->bi_css, struct blkcg, css); - return task_blkcg(current); -} - /** * blkg_to_pdata - get policy private data * @blkg: blkg of interest @@ -253,95 +233,6 @@ static inline void blkg_put(struct blkcg_gq *blkg) __blkg_release(blkg); } -/** - * blk_get_rl - get request_list to use - * @q: request_queue of interest - * @bio: bio which will be attached to the allocated request (may be %NULL) - * - * The caller wants to allocate a request from @q to use for @bio. Find - * the request_list to use and obtain a reference on it. Should be called - * under queue_lock. This function is guaranteed to return non-%NULL - * request_list. - */ -static inline struct request_list *blk_get_rl(struct request_queue *q, - struct bio *bio) -{ - struct blkcg *blkcg; - struct blkcg_gq *blkg; - - rcu_read_lock(); - - blkcg = bio_blkcg(bio); - - /* bypass blkg lookup and use @q->root_rl directly for root */ - if (blkcg == &blkcg_root) - goto root_rl; - - /* - * Try to use blkg->rl. blkg lookup may fail under memory pressure - * or if either the blkcg or queue is going away. Fall back to - * root_rl in such cases. - */ - blkg = blkg_lookup_create(blkcg, q); - if (unlikely(IS_ERR(blkg))) - goto root_rl; - - blkg_get(blkg); - rcu_read_unlock(); - return &blkg->rl; -root_rl: - rcu_read_unlock(); - return &q->root_rl; -} - -/** - * blk_put_rl - put request_list - * @rl: request_list to put - * - * Put the reference acquired by blk_get_rl(). Should be called under - * queue_lock. - */ -static inline void blk_put_rl(struct request_list *rl) -{ - /* root_rl may not have blkg set */ - if (rl->blkg && rl->blkg->blkcg != &blkcg_root) - blkg_put(rl->blkg); -} - -/** - * blk_rq_set_rl - associate a request with a request_list - * @rq: request of interest - * @rl: target request_list - * - * Associate @rq with @rl so that accounting and freeing can know the - * request_list @rq came from. - */ -static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl) -{ - rq->rl = rl; -} - -/** - * blk_rq_rl - return the request_list a request came from - * @rq: request of interest - * - * Return the request_list @rq is allocated from. - */ -static inline struct request_list *blk_rq_rl(struct request *rq) -{ - return rq->rl; -} - -struct request_list *__blk_queue_next_rl(struct request_list *rl, - struct request_queue *q); -/** - * blk_queue_for_each_rl - iterate through all request_lists of a request_queue - * - * Should be used under queue_lock. - */ -#define blk_queue_for_each_rl(rl, q) \ - for ((rl) = &(q)->root_rl; (rl); (rl) = __blk_queue_next_rl((rl), (q))) - /** * blkg_stat_add - add a value to a blkg_stat * @stat: target blkg_stat @@ -460,7 +351,6 @@ static inline void blkg_rwstat_reset(struct blkg_rwstat *rwstat) #else /* CONFIG_BLK_CGROUP */ struct cgroup; -struct blkcg; struct blkg_policy_data { }; @@ -471,6 +361,8 @@ struct blkcg_gq { struct blkcg_policy { }; +static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; } +static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; } static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, void *key) { return NULL; } static inline int blkcg_init_queue(struct request_queue *q) { return 0; } static inline void blkcg_drain_queue(struct request_queue *q) { } @@ -482,9 +374,6 @@ static inline int blkcg_activate_policy(struct request_queue *q, static inline void blkcg_deactivate_policy(struct request_queue *q, const struct blkcg_policy *pol) { } -static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; } -static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; } - static inline struct blkg_policy_data *blkg_to_pd(struct blkcg_gq *blkg, struct blkcg_policy *pol) { return NULL; } static inline struct blkcg_gq *pd_to_blkg(struct blkg_policy_data *pd) { return NULL; } @@ -492,14 +381,5 @@ static inline char *blkg_path(struct blkcg_gq *blkg) { return NULL; } static inline void blkg_get(struct blkcg_gq *blkg) { } static inline void blkg_put(struct blkcg_gq *blkg) { } -static inline struct request_list *blk_get_rl(struct request_queue *q, - struct bio *bio) { return &q->root_rl; } -static inline void blk_put_rl(struct request_list *rl) { } -static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl) { } -static inline struct request_list *blk_rq_rl(struct request *rq) { return &rq->q->root_rl; } - -#define blk_queue_for_each_rl(rl, q) \ - for ((rl) = &(q)->root_rl; (rl); (rl) = NULL) - #endif /* CONFIG_BLK_CGROUP */ #endif /* _BLK_CGROUP_H */ diff --git a/trunk/block/blk-core.c b/trunk/block/blk-core.c index 4b4dbdfbca89..93eb3e4f88ce 100644 --- a/trunk/block/blk-core.c +++ b/trunk/block/blk-core.c @@ -387,7 +387,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) if (!list_empty(&q->queue_head) && q->request_fn) __blk_run_queue(q); - drain |= q->nr_rqs_elvpriv; + drain |= q->rq.elvpriv; /* * Unfortunately, requests are queued at and tracked from @@ -397,7 +397,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) if (drain_all) { drain |= !list_empty(&q->queue_head); for (i = 0; i < 2; i++) { - drain |= q->nr_rqs[i]; + drain |= q->rq.count[i]; drain |= q->in_flight[i]; drain |= !list_empty(&q->flush_queue[i]); } @@ -416,14 +416,9 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) * left with hung waiters. We need to wake up those waiters. */ if (q->request_fn) { - struct request_list *rl; - spin_lock_irq(q->queue_lock); - - blk_queue_for_each_rl(rl, q) - for (i = 0; i < ARRAY_SIZE(rl->wait); i++) - wake_up_all(&rl->wait[i]); - + for (i = 0; i < ARRAY_SIZE(q->rq.wait); i++) + wake_up_all(&q->rq.wait[i]); spin_unlock_irq(q->queue_lock); } } @@ -522,33 +517,28 @@ void blk_cleanup_queue(struct request_queue *q) } EXPORT_SYMBOL(blk_cleanup_queue); -int blk_init_rl(struct request_list *rl, struct request_queue *q, - gfp_t gfp_mask) +static int blk_init_free_list(struct request_queue *q) { + struct request_list *rl = &q->rq; + if (unlikely(rl->rq_pool)) return 0; - rl->q = q; rl->count[BLK_RW_SYNC] = rl->count[BLK_RW_ASYNC] = 0; rl->starved[BLK_RW_SYNC] = rl->starved[BLK_RW_ASYNC] = 0; + rl->elvpriv = 0; init_waitqueue_head(&rl->wait[BLK_RW_SYNC]); init_waitqueue_head(&rl->wait[BLK_RW_ASYNC]); rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, - mempool_free_slab, request_cachep, - gfp_mask, q->node); + mempool_free_slab, request_cachep, q->node); + if (!rl->rq_pool) return -ENOMEM; return 0; } -void blk_exit_rl(struct request_list *rl) -{ - if (rl->rq_pool) - mempool_destroy(rl->rq_pool); -} - struct request_queue *blk_alloc_queue(gfp_t gfp_mask) { return blk_alloc_queue_node(gfp_mask, -1); @@ -690,7 +680,7 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn, if (!q) return NULL; - if (blk_init_rl(&q->root_rl, q, GFP_KERNEL)) + if (blk_init_free_list(q)) return NULL; q->request_fn = rfn; @@ -732,15 +722,15 @@ bool blk_get_queue(struct request_queue *q) } EXPORT_SYMBOL(blk_get_queue); -static inline void blk_free_request(struct request_list *rl, struct request *rq) +static inline void blk_free_request(struct request_queue *q, struct request *rq) { if (rq->cmd_flags & REQ_ELVPRIV) { - elv_put_request(rl->q, rq); + elv_put_request(q, rq); if (rq->elv.icq) put_io_context(rq->elv.icq->ioc); } - mempool_free(rq, rl->rq_pool); + mempool_free(rq, q->rq.rq_pool); } /* @@ -777,23 +767,18 @@ static void ioc_set_batching(struct request_queue *q, struct io_context *ioc) ioc->last_waited = jiffies; } -static void __freed_request(struct request_list *rl, int sync) +static void __freed_request(struct request_queue *q, int sync) { - struct request_queue *q = rl->q; + struct request_list *rl = &q->rq; - /* - * bdi isn't aware of blkcg yet. As all async IOs end up root - * blkcg anyway, just use root blkcg state. - */ - if (rl == &q->root_rl && - rl->count[sync] < queue_congestion_off_threshold(q)) + if (rl->count[sync] < queue_congestion_off_threshold(q)) blk_clear_queue_congested(q, sync); if (rl->count[sync] + 1 <= q->nr_requests) { if (waitqueue_active(&rl->wait[sync])) wake_up(&rl->wait[sync]); - blk_clear_rl_full(rl, sync); + blk_clear_queue_full(q, sync); } } @@ -801,20 +786,19 @@ static void __freed_request(struct request_list *rl, int sync) * A request has just been released. Account for it, update the full and * congestion status, wake up any waiters. Called under q->queue_lock. */ -static void freed_request(struct request_list *rl, unsigned int flags) +static void freed_request(struct request_queue *q, unsigned int flags) { - struct request_queue *q = rl->q; + struct request_list *rl = &q->rq; int sync = rw_is_sync(flags); - q->nr_rqs[sync]--; rl->count[sync]--; if (flags & REQ_ELVPRIV) - q->nr_rqs_elvpriv--; + rl->elvpriv--; - __freed_request(rl, sync); + __freed_request(q, sync); if (unlikely(rl->starved[sync ^ 1])) - __freed_request(rl, sync ^ 1); + __freed_request(q, sync ^ 1); } /* @@ -853,8 +837,8 @@ static struct io_context *rq_ioc(struct bio *bio) } /** - * __get_request - get a free request - * @rl: request list to allocate from + * get_request - get a free request + * @q: request_queue to allocate request from * @rw_flags: RW and SYNC flags * @bio: bio to allocate request for (can be %NULL) * @gfp_mask: allocation mask @@ -866,16 +850,20 @@ static struct io_context *rq_ioc(struct bio *bio) * Returns %NULL on failure, with @q->queue_lock held. * Returns !%NULL on success, with @q->queue_lock *not held*. */ -static struct request *__get_request(struct request_list *rl, int rw_flags, - struct bio *bio, gfp_t gfp_mask) +static struct request *get_request(struct request_queue *q, int rw_flags, + struct bio *bio, gfp_t gfp_mask) { - struct request_queue *q = rl->q; struct request *rq; - struct elevator_type *et = q->elevator->type; - struct io_context *ioc = rq_ioc(bio); + struct request_list *rl = &q->rq; + struct elevator_type *et; + struct io_context *ioc; struct io_cq *icq = NULL; const bool is_sync = rw_is_sync(rw_flags) != 0; + bool retried = false; int may_queue; +retry: + et = q->elevator->type; + ioc = rq_ioc(bio); if (unlikely(blk_queue_dead(q))) return NULL; @@ -886,15 +874,29 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, if (rl->count[is_sync]+1 >= queue_congestion_on_threshold(q)) { if (rl->count[is_sync]+1 >= q->nr_requests) { + /* + * We want ioc to record batching state. If it's + * not already there, creating a new one requires + * dropping queue_lock, which in turn requires + * retesting conditions to avoid queue hang. + */ + if (!ioc && !retried) { + spin_unlock_irq(q->queue_lock); + create_io_context(gfp_mask, q->node); + spin_lock_irq(q->queue_lock); + retried = true; + goto retry; + } + /* * The queue will fill after this allocation, so set * it as full, and mark this process as "batching". * This process will be allowed to complete a batch of * requests, others will be blocked. */ - if (!blk_rl_full(rl, is_sync)) { + if (!blk_queue_full(q, is_sync)) { ioc_set_batching(q, ioc); - blk_set_rl_full(rl, is_sync); + blk_set_queue_full(q, is_sync); } else { if (may_queue != ELV_MQUEUE_MUST && !ioc_batching(q, ioc)) { @@ -907,12 +909,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, } } } - /* - * bdi isn't aware of blkcg yet. As all async IOs end up - * root blkcg anyway, just use root blkcg state. - */ - if (rl == &q->root_rl) - blk_set_queue_congested(q, is_sync); + blk_set_queue_congested(q, is_sync); } /* @@ -923,7 +920,6 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, if (rl->count[is_sync] >= (3 * q->nr_requests / 2)) return NULL; - q->nr_rqs[is_sync]++; rl->count[is_sync]++; rl->starved[is_sync] = 0; @@ -939,7 +935,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, */ if (blk_rq_should_init_elevator(bio) && !blk_queue_bypass(q)) { rw_flags |= REQ_ELVPRIV; - q->nr_rqs_elvpriv++; + rl->elvpriv++; if (et->icq_cache && ioc) icq = ioc_lookup_icq(ioc, q); } @@ -949,19 +945,22 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, spin_unlock_irq(q->queue_lock); /* allocate and init request */ - rq = mempool_alloc(rl->rq_pool, gfp_mask); + rq = mempool_alloc(q->rq.rq_pool, gfp_mask); if (!rq) goto fail_alloc; blk_rq_init(q, rq); - blk_rq_set_rl(rq, rl); rq->cmd_flags = rw_flags | REQ_ALLOCED; /* init elvpriv */ if (rw_flags & REQ_ELVPRIV) { if (unlikely(et->icq_cache && !icq)) { - if (ioc) - icq = ioc_create_icq(ioc, q, gfp_mask); + create_io_context(gfp_mask, q->node); + ioc = rq_ioc(bio); + if (!ioc) + goto fail_elvpriv; + + icq = ioc_create_icq(ioc, q, gfp_mask); if (!icq) goto fail_elvpriv; } @@ -1001,7 +1000,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, rq->elv.icq = NULL; spin_lock_irq(q->queue_lock); - q->nr_rqs_elvpriv--; + rl->elvpriv--; spin_unlock_irq(q->queue_lock); goto out; @@ -1014,7 +1013,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, * queue, but this is pretty rare. */ spin_lock_irq(q->queue_lock); - freed_request(rl, rw_flags); + freed_request(q, rw_flags); /* * in the very unlikely event that allocation failed and no @@ -1030,58 +1029,56 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, } /** - * get_request - get a free request + * get_request_wait - get a free request with retry * @q: request_queue to allocate request from * @rw_flags: RW and SYNC flags * @bio: bio to allocate request for (can be %NULL) - * @gfp_mask: allocation mask * - * Get a free request from @q. If %__GFP_WAIT is set in @gfp_mask, this - * function keeps retrying under memory pressure and fails iff @q is dead. + * Get a free request from @q. This function keeps retrying under memory + * pressure and fails iff @q is dead. * * Must be callled with @q->queue_lock held and, * Returns %NULL on failure, with @q->queue_lock held. * Returns !%NULL on success, with @q->queue_lock *not held*. */ -static struct request *get_request(struct request_queue *q, int rw_flags, - struct bio *bio, gfp_t gfp_mask) +static struct request *get_request_wait(struct request_queue *q, int rw_flags, + struct bio *bio) { const bool is_sync = rw_is_sync(rw_flags) != 0; - DEFINE_WAIT(wait); - struct request_list *rl; struct request *rq; - rl = blk_get_rl(q, bio); /* transferred to @rq on success */ -retry: - rq = __get_request(rl, rw_flags, bio, gfp_mask); - if (rq) - return rq; + rq = get_request(q, rw_flags, bio, GFP_NOIO); + while (!rq) { + DEFINE_WAIT(wait); + struct request_list *rl = &q->rq; - if (!(gfp_mask & __GFP_WAIT) || unlikely(blk_queue_dead(q))) { - blk_put_rl(rl); - return NULL; - } + if (unlikely(blk_queue_dead(q))) + return NULL; - /* wait on @rl and retry */ - prepare_to_wait_exclusive(&rl->wait[is_sync], &wait, - TASK_UNINTERRUPTIBLE); + prepare_to_wait_exclusive(&rl->wait[is_sync], &wait, + TASK_UNINTERRUPTIBLE); - trace_block_sleeprq(q, bio, rw_flags & 1); + trace_block_sleeprq(q, bio, rw_flags & 1); - spin_unlock_irq(q->queue_lock); - io_schedule(); + spin_unlock_irq(q->queue_lock); + io_schedule(); - /* - * After sleeping, we become a "batching" process and will be able - * to allocate at least one request, and up to a big batch of them - * for a small period time. See ioc_batching, ioc_set_batching - */ - ioc_set_batching(q, current->io_context); + /* + * After sleeping, we become a "batching" process and + * will be able to allocate at least one request, and + * up to a big batch of them for a small period time. + * See ioc_batching, ioc_set_batching + */ + create_io_context(GFP_NOIO, q->node); + ioc_set_batching(q, current->io_context); - spin_lock_irq(q->queue_lock); - finish_wait(&rl->wait[is_sync], &wait); + spin_lock_irq(q->queue_lock); + finish_wait(&rl->wait[is_sync], &wait); - goto retry; + rq = get_request(q, rw_flags, bio, GFP_NOIO); + }; + + return rq; } struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask) @@ -1090,11 +1087,11 @@ struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask) BUG_ON(rw != READ && rw != WRITE); - /* create ioc upfront */ - create_io_context(gfp_mask, q->node); - spin_lock_irq(q->queue_lock); - rq = get_request(q, rw, NULL, gfp_mask); + if (gfp_mask & __GFP_WAIT) + rq = get_request_wait(q, rw, NULL); + else + rq = get_request(q, rw, NULL, gfp_mask); if (!rq) spin_unlock_irq(q->queue_lock); /* q->queue_lock is unlocked at this point */ @@ -1251,14 +1248,12 @@ void __blk_put_request(struct request_queue *q, struct request *req) */ if (req->cmd_flags & REQ_ALLOCED) { unsigned int flags = req->cmd_flags; - struct request_list *rl = blk_rq_rl(req); BUG_ON(!list_empty(&req->queuelist)); BUG_ON(!hlist_unhashed(&req->hash)); - blk_free_request(rl, req); - freed_request(rl, flags); - blk_put_rl(rl); + blk_free_request(q, req); + freed_request(q, flags); } } EXPORT_SYMBOL_GPL(__blk_put_request); @@ -1486,7 +1481,7 @@ void blk_queue_bio(struct request_queue *q, struct bio *bio) * Grab a free request. This is might sleep but can not fail. * Returns with the queue unlocked. */ - req = get_request(q, rw_flags, bio, GFP_NOIO); + req = get_request_wait(q, rw_flags, bio); if (unlikely(!req)) { bio_endio(bio, -ENODEV); /* @q is dead */ goto out_unlock; @@ -1707,14 +1702,6 @@ generic_make_request_checks(struct bio *bio) goto end_io; } - /* - * Various block parts want %current->io_context and lazy ioc - * allocation ends up trading a lot of pain for a small amount of - * memory. Just allocate it upfront. This may fail and block - * layer knows how to live with it. - */ - create_io_context(GFP_ATOMIC, q->node); - if (blk_throtl_bio(q, bio)) return false; /* throttled, will be resubmitted later */ @@ -2909,47 +2896,23 @@ static void queue_unplugged(struct request_queue *q, unsigned int depth, } -static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule) +static void flush_plug_callbacks(struct blk_plug *plug) { LIST_HEAD(callbacks); - while (!list_empty(&plug->cb_list)) { - list_splice_init(&plug->cb_list, &callbacks); + if (list_empty(&plug->cb_list)) + return; - while (!list_empty(&callbacks)) { - struct blk_plug_cb *cb = list_first_entry(&callbacks, + list_splice_init(&plug->cb_list, &callbacks); + + while (!list_empty(&callbacks)) { + struct blk_plug_cb *cb = list_first_entry(&callbacks, struct blk_plug_cb, list); - list_del(&cb->list); - cb->callback(cb, from_schedule); - } - } -} - -struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug, void *data, - int size) -{ - struct blk_plug *plug = current->plug; - struct blk_plug_cb *cb; - - if (!plug) - return NULL; - - list_for_each_entry(cb, &plug->cb_list, list) - if (cb->callback == unplug && cb->data == data) - return cb; - - /* Not currently on the callback list */ - BUG_ON(size < sizeof(*cb)); - cb = kzalloc(size, GFP_ATOMIC); - if (cb) { - cb->data = data; - cb->callback = unplug; - list_add(&cb->list, &plug->cb_list); + list_del(&cb->list); + cb->callback(cb); } - return cb; } -EXPORT_SYMBOL(blk_check_plugged); void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) { @@ -2961,7 +2924,7 @@ void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) BUG_ON(plug->magic != PLUG_MAGIC); - flush_plug_callbacks(plug, from_schedule); + flush_plug_callbacks(plug); if (list_empty(&plug->list)) return; diff --git a/trunk/block/blk-ioc.c b/trunk/block/blk-ioc.c index fab4cdd3f7bb..893b8007c657 100644 --- a/trunk/block/blk-ioc.c +++ b/trunk/block/blk-ioc.c @@ -244,7 +244,6 @@ int create_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node) /* initialize */ atomic_long_set(&ioc->refcount, 1); - atomic_set(&ioc->nr_tasks, 1); atomic_set(&ioc->active_ref, 1); spin_lock_init(&ioc->lock); INIT_RADIX_TREE(&ioc->icq_tree, GFP_ATOMIC | __GFP_HIGH); diff --git a/trunk/block/blk-settings.c b/trunk/block/blk-settings.c index 565a6786032f..d3234fc494ad 100644 --- a/trunk/block/blk-settings.c +++ b/trunk/block/blk-settings.c @@ -143,7 +143,8 @@ void blk_set_stacking_limits(struct queue_limits *lim) lim->discard_zeroes_data = 1; lim->max_segments = USHRT_MAX; lim->max_hw_sectors = UINT_MAX; - lim->max_sectors = UINT_MAX; + + lim->max_sectors = BLK_DEF_MAX_SECTORS; } EXPORT_SYMBOL(blk_set_stacking_limits); diff --git a/trunk/block/blk-sysfs.c b/trunk/block/blk-sysfs.c index 9628b291f960..aa41b47c22d2 100644 --- a/trunk/block/blk-sysfs.c +++ b/trunk/block/blk-sysfs.c @@ -40,7 +40,7 @@ static ssize_t queue_requests_show(struct request_queue *q, char *page) static ssize_t queue_requests_store(struct request_queue *q, const char *page, size_t count) { - struct request_list *rl; + struct request_list *rl = &q->rq; unsigned long nr; int ret; @@ -55,9 +55,6 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) q->nr_requests = nr; blk_queue_congestion_threshold(q); - /* congestion isn't cgroup aware and follows root blkcg for now */ - rl = &q->root_rl; - if (rl->count[BLK_RW_SYNC] >= queue_congestion_on_threshold(q)) blk_set_queue_congested(q, BLK_RW_SYNC); else if (rl->count[BLK_RW_SYNC] < queue_congestion_off_threshold(q)) @@ -68,22 +65,19 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) else if (rl->count[BLK_RW_ASYNC] < queue_congestion_off_threshold(q)) blk_clear_queue_congested(q, BLK_RW_ASYNC); - blk_queue_for_each_rl(rl, q) { - if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { - blk_set_rl_full(rl, BLK_RW_SYNC); - } else { - blk_clear_rl_full(rl, BLK_RW_SYNC); - wake_up(&rl->wait[BLK_RW_SYNC]); - } - - if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { - blk_set_rl_full(rl, BLK_RW_ASYNC); - } else { - blk_clear_rl_full(rl, BLK_RW_ASYNC); - wake_up(&rl->wait[BLK_RW_ASYNC]); - } + if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { + blk_set_queue_full(q, BLK_RW_SYNC); + } else { + blk_clear_queue_full(q, BLK_RW_SYNC); + wake_up(&rl->wait[BLK_RW_SYNC]); } + if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { + blk_set_queue_full(q, BLK_RW_ASYNC); + } else { + blk_clear_queue_full(q, BLK_RW_ASYNC); + wake_up(&rl->wait[BLK_RW_ASYNC]); + } spin_unlock_irq(q->queue_lock); return ret; } @@ -482,6 +476,7 @@ static void blk_release_queue(struct kobject *kobj) { struct request_queue *q = container_of(kobj, struct request_queue, kobj); + struct request_list *rl = &q->rq; blk_sync_queue(q); @@ -494,7 +489,8 @@ static void blk_release_queue(struct kobject *kobj) elevator_exit(q->elevator); } - blk_exit_rl(&q->root_rl); + if (rl->rq_pool) + mempool_destroy(rl->rq_pool); if (q->queue_tags) __blk_queue_free_tags(q); diff --git a/trunk/block/blk-throttle.c b/trunk/block/blk-throttle.c index e287c19908c8..5b0659512047 100644 --- a/trunk/block/blk-throttle.c +++ b/trunk/block/blk-throttle.c @@ -1123,6 +1123,9 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) goto out; } + /* bio_associate_current() needs ioc, try creating */ + create_io_context(GFP_ATOMIC, q->node); + /* * A throtl_grp pointer retrieved under rcu can be used to access * basic fields like stats and io rates. If a group has no rules, diff --git a/trunk/block/blk.h b/trunk/block/blk.h index 2a0ea32d249f..85f6ae42f7d3 100644 --- a/trunk/block/blk.h +++ b/trunk/block/blk.h @@ -18,9 +18,6 @@ static inline void __blk_get_queue(struct request_queue *q) kobject_get(&q->kobj); } -int blk_init_rl(struct request_list *rl, struct request_queue *q, - gfp_t gfp_mask); -void blk_exit_rl(struct request_list *rl); void init_request_from_bio(struct request *req, struct bio *bio); void blk_rq_bio_prep(struct request_queue *q, struct request *rq, struct bio *bio); @@ -36,6 +33,7 @@ bool __blk_end_bidi_request(struct request *rq, int error, void blk_rq_timed_out_timer(unsigned long data); void blk_delete_timer(struct request *); void blk_add_timer(struct request *); +void __generic_unplug_device(struct request_queue *); /* * Internal atomic flags for request handling diff --git a/trunk/block/bsg-lib.c b/trunk/block/bsg-lib.c index deee61fbb741..7ad49c88f6b1 100644 --- a/trunk/block/bsg-lib.c +++ b/trunk/block/bsg-lib.c @@ -243,3 +243,56 @@ int bsg_setup_queue(struct device *dev, struct request_queue *q, return 0; } EXPORT_SYMBOL_GPL(bsg_setup_queue); + +/** + * bsg_remove_queue - Deletes the bsg dev from the q + * @q: the request_queue that is to be torn down. + * + * Notes: + * Before unregistering the queue empty any requests that are blocked + */ +void bsg_remove_queue(struct request_queue *q) +{ + struct request *req; /* block request */ + int counts; /* totals for request_list count and starved */ + + if (!q) + return; + + /* Stop taking in new requests */ + spin_lock_irq(q->queue_lock); + blk_stop_queue(q); + + /* drain all requests in the queue */ + while (1) { + /* need the lock to fetch a request + * this may fetch the same reqeust as the previous pass + */ + req = blk_fetch_request(q); + /* save requests in use and starved */ + counts = q->rq.count[0] + q->rq.count[1] + + q->rq.starved[0] + q->rq.starved[1]; + spin_unlock_irq(q->queue_lock); + /* any requests still outstanding? */ + if (counts == 0) + break; + + /* This may be the same req as the previous iteration, + * always send the blk_end_request_all after a prefetch. + * It is not okay to not end the request because the + * prefetch started the request. + */ + if (req) { + /* return -ENXIO to indicate that this queue is + * going away + */ + req->errors = -ENXIO; + blk_end_request_all(req, -ENXIO); + } + + msleep(200); /* allow bsg to possibly finish */ + spin_lock_irq(q->queue_lock); + } + bsg_unregister_queue(q); +} +EXPORT_SYMBOL_GPL(bsg_remove_queue); diff --git a/trunk/block/genhd.c b/trunk/block/genhd.c index cac7366957c3..9cf5583c90ff 100644 --- a/trunk/block/genhd.c +++ b/trunk/block/genhd.c @@ -154,7 +154,7 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) part = rcu_dereference(ptbl->part[piter->idx]); if (!part) continue; - if (!part_nr_sects_read(part) && + if (!part->nr_sects && !(piter->flags & DISK_PITER_INCL_EMPTY) && !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 && piter->idx == 0)) @@ -191,7 +191,7 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit); static inline int sector_in_part(struct hd_struct *part, sector_t sector) { return part->start_sect <= sector && - sector < part->start_sect + part_nr_sects_read(part); + sector < part->start_sect + part->nr_sects; } /** @@ -769,8 +769,8 @@ void __init printk_all_partitions(void) printk("%s%s %10llu %s %s", is_part0 ? "" : " ", bdevt_str(part_devt(part), devt_buf), - (unsigned long long)part_nr_sects_read(part) >> 1 - , disk_name(disk, part->partno, name_buf), + (unsigned long long)part->nr_sects >> 1, + disk_name(disk, part->partno, name_buf), uuid_buf); if (is_part0) { if (disk->driverfs_dev != NULL && @@ -862,7 +862,7 @@ static int show_partition(struct seq_file *seqf, void *v) while ((part = disk_part_iter_next(&piter))) seq_printf(seqf, "%4d %7d %10llu %s\n", MAJOR(part_devt(part)), MINOR(part_devt(part)), - (unsigned long long)part_nr_sects_read(part) >> 1, + (unsigned long long)part->nr_sects >> 1, disk_name(sgp, part->partno, buf)); disk_part_iter_exit(&piter); @@ -1268,16 +1268,6 @@ struct gendisk *alloc_disk_node(int minors, int node_id) } disk->part_tbl->part[0] = &disk->part0; - /* - * set_capacity() and get_capacity() currently don't use - * seqcounter to read/update the part0->nr_sects. Still init - * the counter as we can read the sectors in IO submission - * patch using seqence counters. - * - * TODO: Ideally set_capacity() and get_capacity() should be - * converted to make use of bd_mutex and sequence counters. - */ - seqcount_init(&disk->part0.nr_sects_seq); hd_ref_init(&disk->part0); disk->minors = minors; diff --git a/trunk/block/ioctl.c b/trunk/block/ioctl.c index 4476e0e85d16..ba15b2dbfb98 100644 --- a/trunk/block/ioctl.c +++ b/trunk/block/ioctl.c @@ -13,7 +13,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user { struct block_device *bdevp; struct gendisk *disk; - struct hd_struct *part, *lpart; + struct hd_struct *part; struct blkpg_ioctl_arg a; struct blkpg_partition p; struct disk_part_iter piter; @@ -36,8 +36,8 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user case BLKPG_ADD_PARTITION: start = p.start >> 9; length = p.length >> 9; - /* check for fit in a hd_struct */ - if (sizeof(sector_t) == sizeof(long) && + /* check for fit in a hd_struct */ + if (sizeof(sector_t) == sizeof(long) && sizeof(long long) > sizeof(long)) { long pstart = start, plength = length; if (pstart != start || plength != length @@ -91,59 +91,6 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user mutex_unlock(&bdevp->bd_mutex); bdput(bdevp); - return 0; - case BLKPG_RESIZE_PARTITION: - start = p.start >> 9; - /* new length of partition in bytes */ - length = p.length >> 9; - /* check for fit in a hd_struct */ - if (sizeof(sector_t) == sizeof(long) && - sizeof(long long) > sizeof(long)) { - long pstart = start, plength = length; - if (pstart != start || plength != length - || pstart < 0 || plength < 0) - return -EINVAL; - } - part = disk_get_part(disk, partno); - if (!part) - return -ENXIO; - bdevp = bdget(part_devt(part)); - if (!bdevp) { - disk_put_part(part); - return -ENOMEM; - } - mutex_lock(&bdevp->bd_mutex); - mutex_lock_nested(&bdev->bd_mutex, 1); - if (start != part->start_sect) { - mutex_unlock(&bdevp->bd_mutex); - mutex_unlock(&bdev->bd_mutex); - bdput(bdevp); - disk_put_part(part); - return -EINVAL; - } - /* overlap? */ - disk_part_iter_init(&piter, disk, - DISK_PITER_INCL_EMPTY); - while ((lpart = disk_part_iter_next(&piter))) { - if (lpart->partno != partno && - !(start + length <= lpart->start_sect || - start >= lpart->start_sect + lpart->nr_sects) - ) { - disk_part_iter_exit(&piter); - mutex_unlock(&bdevp->bd_mutex); - mutex_unlock(&bdev->bd_mutex); - bdput(bdevp); - disk_put_part(part); - return -EBUSY; - } - } - disk_part_iter_exit(&piter); - part_nr_sects_write(part, (sector_t)length); - i_size_write(bdevp->bd_inode, p.length); - mutex_unlock(&bdevp->bd_mutex); - mutex_unlock(&bdev->bd_mutex); - bdput(bdevp); - disk_put_part(part); return 0; default: return -EINVAL; diff --git a/trunk/block/partition-generic.c b/trunk/block/partition-generic.c index f1d14519cc04..6df5d6928a44 100644 --- a/trunk/block/partition-generic.c +++ b/trunk/block/partition-generic.c @@ -84,7 +84,7 @@ ssize_t part_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); - return sprintf(buf, "%llu\n",(unsigned long long)part_nr_sects_read(p)); + return sprintf(buf, "%llu\n",(unsigned long long)p->nr_sects); } static ssize_t part_ro_show(struct device *dev, @@ -294,8 +294,6 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, err = -ENOMEM; goto out_free; } - - seqcount_init(&p->nr_sects_seq); pdev = part_to_dev(p); p->start_sect = start; diff --git a/trunk/drivers/Kconfig b/trunk/drivers/Kconfig index ece958d3762e..805c432c9439 100644 --- a/trunk/drivers/Kconfig +++ b/trunk/drivers/Kconfig @@ -112,8 +112,6 @@ source "drivers/auxdisplay/Kconfig" source "drivers/uio/Kconfig" -source "drivers/vfio/Kconfig" - source "drivers/vlynq/Kconfig" source "drivers/virtio/Kconfig" diff --git a/trunk/drivers/Makefile b/trunk/drivers/Makefile index 5b421840c48d..bd36f09f2246 100644 --- a/trunk/drivers/Makefile +++ b/trunk/drivers/Makefile @@ -60,7 +60,6 @@ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ obj-y += firewire/ obj-$(CONFIG_UIO) += uio/ -obj-$(CONFIG_VFIO) += vfio/ obj-y += cdrom/ obj-y += auxdisplay/ obj-$(CONFIG_PCCARD) += pcmcia/ diff --git a/trunk/drivers/base/Kconfig b/trunk/drivers/base/Kconfig index 08b4c5209384..9b21469482ae 100644 --- a/trunk/drivers/base/Kconfig +++ b/trunk/drivers/base/Kconfig @@ -196,7 +196,6 @@ config CMA bool "Contiguous Memory Allocator (EXPERIMENTAL)" depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL select MIGRATION - select MEMORY_ISOLATION help This enables the Contiguous Memory Allocator which allows drivers to allocate big physically-contiguous blocks of memory for use with diff --git a/trunk/drivers/base/devtmpfs.c b/trunk/drivers/base/devtmpfs.c index deb4a456cf83..d91a3a0b2325 100644 --- a/trunk/drivers/base/devtmpfs.c +++ b/trunk/drivers/base/devtmpfs.c @@ -156,7 +156,9 @@ static int dev_mkdir(const char *name, umode_t mode) if (!err) /* mark as kernel-created inode */ dentry->d_inode->i_private = &thread; - done_path_create(&path, dentry); + dput(dentry); + mutex_unlock(&path.dentry->d_inode->i_mutex); + path_put(&path); return err; } @@ -216,7 +218,10 @@ static int handle_create(const char *nodename, umode_t mode, struct device *dev) /* mark as kernel-created inode */ dentry->d_inode->i_private = &thread; } - done_path_create(&path, dentry); + dput(dentry); + + mutex_unlock(&path.dentry->d_inode->i_mutex); + path_put(&path); return err; } diff --git a/trunk/drivers/block/drbd/drbd_actlog.c b/trunk/drivers/block/drbd/drbd_actlog.c index 3fbef018ce55..e54e31b02b88 100644 --- a/trunk/drivers/block/drbd/drbd_actlog.c +++ b/trunk/drivers/block/drbd/drbd_actlog.c @@ -411,7 +411,7 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused) + mdev->ldev->md.al_offset + mdev->al_tr_pos; if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) - drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR); + drbd_chk_io_error(mdev, 1, true); if (++mdev->al_tr_pos > div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT)) @@ -876,11 +876,7 @@ int __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size, unsigned int enr, count = 0; struct lc_element *e; - /* this should be an empty REQ_FLUSH */ - if (size == 0) - return 0; - - if (size < 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) { + if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) { dev_err(DEV, "sector: %llus, size: %d\n", (unsigned long long)sector, size); return 0; diff --git a/trunk/drivers/block/drbd/drbd_bitmap.c b/trunk/drivers/block/drbd/drbd_bitmap.c index ba91b408abad..fcb956bb4b4c 100644 --- a/trunk/drivers/block/drbd/drbd_bitmap.c +++ b/trunk/drivers/block/drbd/drbd_bitmap.c @@ -1096,7 +1096,7 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned flags, unsigned lazy_w if (ctx->error) { dev_alert(DEV, "we had at least one MD IO ERROR during bitmap IO\n"); - drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR); + drbd_chk_io_error(mdev, 1, true); err = -EIO; /* ctx->error ? */ } @@ -1212,7 +1212,7 @@ int drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(loc wait_until_done_or_disk_failure(mdev, mdev->ldev, &ctx->done); if (ctx->error) - drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR); + drbd_chk_io_error(mdev, 1, true); /* that should force detach, so the in memory bitmap will be * gone in a moment as well. */ diff --git a/trunk/drivers/block/drbd/drbd_int.h b/trunk/drivers/block/drbd/drbd_int.h index b2ca143d0053..02f013a073a7 100644 --- a/trunk/drivers/block/drbd/drbd_int.h +++ b/trunk/drivers/block/drbd/drbd_int.h @@ -813,6 +813,7 @@ enum { SIGNAL_ASENDER, /* whether asender wants to be interrupted */ SEND_PING, /* whether asender should send a ping asap */ + UNPLUG_QUEUED, /* only relevant with kernel 2.4 */ UNPLUG_REMOTE, /* sending a "UnplugRemote" could help */ MD_DIRTY, /* current uuids and flags not yet on disk */ DISCARD_CONCURRENT, /* Set on one node, cleared on the peer! */ @@ -823,6 +824,7 @@ enum { CRASHED_PRIMARY, /* This node was a crashed primary. * Gets cleared when the state.conn * goes into C_CONNECTED state. */ + NO_BARRIER_SUPP, /* underlying block device doesn't implement barriers */ CONSIDER_RESYNC, MD_NO_FUA, /* Users wants us to not use FUA/FLUSH on meta data dev */ @@ -832,7 +834,6 @@ enum { BITMAP_IO_QUEUED, /* Started bitmap IO */ GO_DISKLESS, /* Disk is being detached, on io-error or admin request. */ WAS_IO_ERROR, /* Local disk failed returned IO error */ - FORCE_DETACH, /* Force-detach from local disk, aborting any pending local IO */ RESYNC_AFTER_NEG, /* Resync after online grow after the attach&negotiate finished. */ NET_CONGESTED, /* The data socket is congested */ @@ -850,13 +851,6 @@ enum { AL_SUSPENDED, /* Activity logging is currently suspended. */ AHEAD_TO_SYNC_SOURCE, /* Ahead -> SyncSource queued */ STATE_SENT, /* Do not change state/UUIDs while this is set */ - - CALLBACK_PENDING, /* Whether we have a call_usermodehelper(, UMH_WAIT_PROC) - * pending, from drbd worker context. - * If set, bdi_write_congested() returns true, - * so shrink_page_list() would not recurse into, - * and potentially deadlock on, this drbd worker. - */ }; struct drbd_bitmap; /* opaque for drbd_conf */ @@ -1136,8 +1130,8 @@ struct drbd_conf { int rs_in_flight; /* resync sectors in flight (to proxy, in proxy and from proxy) */ int rs_planed; /* resync sectors already planned */ atomic_t ap_in_flight; /* App sectors in flight (waiting for ack) */ - unsigned int peer_max_bio_size; - unsigned int local_max_bio_size; + int peer_max_bio_size; + int local_max_bio_size; }; static inline struct drbd_conf *minor_to_mdev(unsigned int minor) @@ -1441,9 +1435,9 @@ struct bm_extent { * hash table. */ #define HT_SHIFT 8 #define DRBD_MAX_BIO_SIZE (1U<<(9+HT_SHIFT)) -#define DRBD_MAX_BIO_SIZE_SAFE (1U << 12) /* Works always = 4k */ +#define DRBD_MAX_BIO_SIZE_SAFE (1 << 12) /* Works always = 4k */ -#define DRBD_MAX_SIZE_H80_PACKET (1U << 15) /* The old header only allows packets up to 32Kib data */ +#define DRBD_MAX_SIZE_H80_PACKET (1 << 15) /* The old header only allows packets up to 32Kib data */ /* Number of elements in the app_reads_hash */ #define APP_R_HSIZE 15 @@ -1846,20 +1840,12 @@ static inline int drbd_request_state(struct drbd_conf *mdev, return _drbd_request_state(mdev, mask, val, CS_VERBOSE + CS_ORDERED); } -enum drbd_force_detach_flags { - DRBD_IO_ERROR, - DRBD_META_IO_ERROR, - DRBD_FORCE_DETACH, -}; - #define __drbd_chk_io_error(m,f) __drbd_chk_io_error_(m,f, __func__) -static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, - enum drbd_force_detach_flags forcedetach, - const char *where) +static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach, const char *where) { switch (mdev->ldev->dc.on_io_error) { case EP_PASS_ON: - if (forcedetach == DRBD_IO_ERROR) { + if (!forcedetach) { if (__ratelimit(&drbd_ratelimit_state)) dev_err(DEV, "Local IO failed in %s.\n", where); if (mdev->state.disk > D_INCONSISTENT) @@ -1870,8 +1856,6 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, case EP_DETACH: case EP_CALL_HELPER: set_bit(WAS_IO_ERROR, &mdev->flags); - if (forcedetach == DRBD_FORCE_DETACH) - set_bit(FORCE_DETACH, &mdev->flags); if (mdev->state.disk > D_FAILED) { _drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL); dev_err(DEV, @@ -1891,7 +1875,7 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, */ #define drbd_chk_io_error(m,e,f) drbd_chk_io_error_(m,e,f, __func__) static inline void drbd_chk_io_error_(struct drbd_conf *mdev, - int error, enum drbd_force_detach_flags forcedetach, const char *where) + int error, int forcedetach, const char *where) { if (error) { unsigned long flags; @@ -2421,17 +2405,15 @@ static inline void dec_ap_bio(struct drbd_conf *mdev) int ap_bio = atomic_dec_return(&mdev->ap_bio_cnt); D_ASSERT(ap_bio >= 0); - - if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) { - if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags)) - drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w); - } - /* this currently does wake_up for every dec_ap_bio! * maybe rather introduce some type of hysteresis? * e.g. (ap_bio == mxb/2 || ap_bio == 0) ? */ if (ap_bio < mxb) wake_up(&mdev->misc_wait); + if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) { + if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags)) + drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w); + } } static inline int drbd_set_ed_uuid(struct drbd_conf *mdev, u64 val) diff --git a/trunk/drivers/block/drbd/drbd_main.c b/trunk/drivers/block/drbd/drbd_main.c index 2e0e7fc1dbba..920ede2829d6 100644 --- a/trunk/drivers/block/drbd/drbd_main.c +++ b/trunk/drivers/block/drbd/drbd_main.c @@ -1514,13 +1514,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, /* Do not change the order of the if above and the two below... */ if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) { /* attach on the peer */ - /* we probably will start a resync soon. - * make sure those things are properly reset. */ - mdev->rs_total = 0; - mdev->rs_failed = 0; - atomic_set(&mdev->rs_pending_cnt, 0); - drbd_rs_cancel_all(mdev); - drbd_send_uuids(mdev); drbd_send_state(mdev, ns); } @@ -1637,24 +1630,9 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, eh = mdev->ldev->dc.on_io_error; was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags); - if (was_io_error && eh == EP_CALL_HELPER) - drbd_khelper(mdev, "local-io-error"); - - /* Immediately allow completion of all application IO, - * that waits for completion from the local disk, - * if this was a force-detach due to disk_timeout - * or administrator request (drbdsetup detach --force). - * Do NOT abort otherwise. - * Aborting local requests may cause serious problems, - * if requests are completed to upper layers already, - * and then later the already submitted local bio completes. - * This can cause DMA into former bio pages that meanwhile - * have been re-used for other things. - * So aborting local requests may cause crashes, - * or even worse, silent data corruption. - */ - if (test_and_clear_bit(FORCE_DETACH, &mdev->flags)) - tl_abort_disk_io(mdev); + /* Immediately allow completion of all application IO, that waits + for completion from the local disk. */ + tl_abort_disk_io(mdev); /* current state still has to be D_FAILED, * there is only one way out: to D_DISKLESS, @@ -1675,6 +1653,9 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, drbd_md_sync(mdev); } put_ldev(mdev); + + if (was_io_error && eh == EP_CALL_HELPER) + drbd_khelper(mdev, "local-io-error"); } /* second half of local IO error, failure to attach, @@ -1688,6 +1669,10 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, "ASSERT FAILED: disk is %s while going diskless\n", drbd_disk_str(mdev->state.disk)); + mdev->rs_total = 0; + mdev->rs_failed = 0; + atomic_set(&mdev->rs_pending_cnt, 0); + if (ns.conn >= C_CONNECTED) drbd_send_state(mdev, ns); @@ -2209,8 +2194,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl { struct p_sizes p; sector_t d_size, u_size; - int q_order_type; - unsigned int max_bio_size; + int q_order_type, max_bio_size; int ok; if (get_ldev_if_state(mdev, D_NEGOTIATING)) { @@ -2219,7 +2203,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl u_size = mdev->ldev->dc.disk_size; q_order_type = drbd_queue_order_type(mdev); max_bio_size = queue_max_hw_sectors(mdev->ldev->backing_bdev->bd_disk->queue) << 9; - max_bio_size = min(max_bio_size, DRBD_MAX_BIO_SIZE); + max_bio_size = min_t(int, max_bio_size, DRBD_MAX_BIO_SIZE); put_ldev(mdev); } else { d_size = 0; @@ -2230,7 +2214,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl /* Never allow old drbd (up to 8.3.7) to see more than 32KiB */ if (mdev->agreed_pro_version <= 94) - max_bio_size = min(max_bio_size, DRBD_MAX_SIZE_H80_PACKET); + max_bio_size = min_t(int, max_bio_size, DRBD_MAX_SIZE_H80_PACKET); p.d_size = cpu_to_be64(d_size); p.u_size = cpu_to_be64(u_size); @@ -3557,22 +3541,6 @@ static int drbd_congested(void *congested_data, int bdi_bits) goto out; } - if (test_bit(CALLBACK_PENDING, &mdev->flags)) { - r |= (1 << BDI_async_congested); - /* Without good local data, we would need to read from remote, - * and that would need the worker thread as well, which is - * currently blocked waiting for that usermode helper to - * finish. - */ - if (!get_ldev_if_state(mdev, D_UP_TO_DATE)) - r |= (1 << BDI_sync_congested); - else - put_ldev(mdev); - r &= bdi_bits; - reason = 'c'; - goto out; - } - if (get_ldev(mdev)) { q = bdev_get_queue(mdev->ldev->backing_bdev); r = bdi_congested(&q->backing_dev_info, bdi_bits); @@ -3636,7 +3604,6 @@ struct drbd_conf *drbd_new_device(unsigned int minor) q->backing_dev_info.congested_data = mdev; blk_queue_make_request(q, drbd_make_request); - blk_queue_flush(q, REQ_FLUSH | REQ_FUA); /* Setting the max_hw_sectors to an odd value of 8kibyte here This triggers a max_bio_size message upon first attach or connect */ blk_queue_max_hw_sectors(q, DRBD_MAX_BIO_SIZE_SAFE >> 8); @@ -3903,7 +3870,7 @@ void drbd_md_sync(struct drbd_conf *mdev) if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) { /* this was a try anyways ... */ dev_err(DEV, "meta data update failed!\n"); - drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR); + drbd_chk_io_error(mdev, 1, true); } /* Update mdev->ldev->md.la_size_sect, @@ -3983,9 +3950,9 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev) spin_lock_irq(&mdev->req_lock); if (mdev->state.conn < C_CONNECTED) { - unsigned int peer; + int peer; peer = be32_to_cpu(buffer->la_peer_max_bio_size); - peer = max(peer, DRBD_MAX_BIO_SIZE_SAFE); + peer = max_t(int, peer, DRBD_MAX_BIO_SIZE_SAFE); mdev->peer_max_bio_size = peer; } spin_unlock_irq(&mdev->req_lock); diff --git a/trunk/drivers/block/drbd/drbd_nl.c b/trunk/drivers/block/drbd/drbd_nl.c index fb9dce8daa24..6d4de6a72e80 100644 --- a/trunk/drivers/block/drbd/drbd_nl.c +++ b/trunk/drivers/block/drbd/drbd_nl.c @@ -147,9 +147,6 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd) char *argv[] = {usermode_helper, cmd, mb, NULL }; int ret; - if (current == mdev->worker.task) - set_bit(CALLBACK_PENDING, &mdev->flags); - snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev)); if (get_net_conf(mdev)) { @@ -192,9 +189,6 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd) usermode_helper, cmd, mb, (ret >> 8) & 0xff, ret); - if (current == mdev->worker.task) - clear_bit(CALLBACK_PENDING, &mdev->flags); - if (ret < 0) /* Ignore any ERRNOs we got. */ ret = 0; @@ -801,8 +795,8 @@ static int drbd_check_al_size(struct drbd_conf *mdev) static void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_bio_size) { struct request_queue * const q = mdev->rq_queue; - unsigned int max_hw_sectors = max_bio_size >> 9; - unsigned int max_segments = 0; + int max_hw_sectors = max_bio_size >> 9; + int max_segments = 0; if (get_ldev_if_state(mdev, D_ATTACHING)) { struct request_queue * const b = mdev->ldev->backing_bdev->bd_disk->queue; @@ -835,7 +829,7 @@ static void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_bio_ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev) { - unsigned int now, new, local, peer; + int now, new, local, peer; now = queue_max_hw_sectors(mdev->rq_queue) << 9; local = mdev->local_max_bio_size; /* Eventually last known value, from volatile memory */ @@ -846,14 +840,13 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev) mdev->local_max_bio_size = local; put_ldev(mdev); } - local = min(local, DRBD_MAX_BIO_SIZE); /* We may ignore peer limits if the peer is modern enough. Because new from 8.3.8 onwards the peer can use multiple BIOs for a single peer_request */ if (mdev->state.conn >= C_CONNECTED) { if (mdev->agreed_pro_version < 94) { - peer = min(mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET); + peer = min_t(int, mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET); /* Correct old drbd (up to 8.3.7) if it believes it can do more than 32KiB */ } else if (mdev->agreed_pro_version == 94) peer = DRBD_MAX_SIZE_H80_PACKET; @@ -861,10 +854,10 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev) peer = DRBD_MAX_BIO_SIZE; } - new = min(local, peer); + new = min_t(int, local, peer); if (mdev->state.role == R_PRIMARY && new < now) - dev_err(DEV, "ASSERT FAILED new < now; (%u < %u)\n", new, now); + dev_err(DEV, "ASSERT FAILED new < now; (%d < %d)\n", new, now); if (new != now) dev_info(DEV, "max BIO size = %u\n", new); @@ -957,14 +950,6 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp * to realize a "hot spare" feature (not that I'd recommend that) */ wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); - /* make sure there is no leftover from previous force-detach attempts */ - clear_bit(FORCE_DETACH, &mdev->flags); - - /* and no leftover from previously aborted resync or verify, either */ - mdev->rs_total = 0; - mdev->rs_failed = 0; - atomic_set(&mdev->rs_pending_cnt, 0); - /* allocation not in the IO path, cqueue thread context */ nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL); if (!nbc) { @@ -1360,7 +1345,6 @@ static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, } if (dt.detach_force) { - set_bit(FORCE_DETACH, &mdev->flags); drbd_force_state(mdev, NS(disk, D_FAILED)); reply->ret_code = SS_SUCCESS; goto out; @@ -1978,11 +1962,9 @@ static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl int retcode; /* If there is still bitmap IO pending, probably because of a previous - * resync just being finished, wait for it before requesting a new resync. - * Also wait for it's after_state_ch(). */ + * resync just being finished, wait for it before requesting a new resync. */ drbd_suspend_io(mdev); wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags)); - drbd_flush_workqueue(mdev); retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED); @@ -2021,11 +2003,9 @@ static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re int retcode; /* If there is still bitmap IO pending, probably because of a previous - * resync just being finished, wait for it before requesting a new resync. - * Also wait for it's after_state_ch(). */ + * resync just being finished, wait for it before requesting a new resync. */ drbd_suspend_io(mdev); wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags)); - drbd_flush_workqueue(mdev); retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S), CS_ORDERED); diff --git a/trunk/drivers/block/drbd/drbd_proc.c b/trunk/drivers/block/drbd/drbd_proc.c index 5496104f90b9..869bada2ed06 100644 --- a/trunk/drivers/block/drbd/drbd_proc.c +++ b/trunk/drivers/block/drbd/drbd_proc.c @@ -245,9 +245,6 @@ static int drbd_seq_show(struct seq_file *seq, void *v) mdev->state.role == R_SECONDARY) { seq_printf(seq, "%2d: cs:Unconfigured\n", i); } else { - /* reset mdev->congestion_reason */ - bdi_rw_congested(&mdev->rq_queue->backing_dev_info); - seq_printf(seq, "%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c%c\n" " ns:%u nr:%u dw:%u dr:%u al:%u bm:%u " diff --git a/trunk/drivers/block/drbd/drbd_receiver.c b/trunk/drivers/block/drbd/drbd_receiver.c index c74ca2df7431..ea4836e0ae98 100644 --- a/trunk/drivers/block/drbd/drbd_receiver.c +++ b/trunk/drivers/block/drbd/drbd_receiver.c @@ -277,9 +277,6 @@ static void drbd_pp_free(struct drbd_conf *mdev, struct page *page, int is_net) atomic_t *a = is_net ? &mdev->pp_in_use_by_net : &mdev->pp_in_use; int i; - if (page == NULL) - return; - if (drbd_pp_vacant > (DRBD_MAX_BIO_SIZE/PAGE_SIZE)*minor_count) i = page_chain_free(page); else { @@ -319,7 +316,7 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev, gfp_t gfp_mask) __must_hold(local) { struct drbd_epoch_entry *e; - struct page *page = NULL; + struct page *page; unsigned nr_pages = (data_size + PAGE_SIZE -1) >> PAGE_SHIFT; if (drbd_insert_fault(mdev, DRBD_FAULT_AL_EE)) @@ -332,11 +329,9 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev, return NULL; } - if (data_size) { - page = drbd_pp_alloc(mdev, nr_pages, (gfp_mask & __GFP_WAIT)); - if (!page) - goto fail; - } + page = drbd_pp_alloc(mdev, nr_pages, (gfp_mask & __GFP_WAIT)); + if (!page) + goto fail; INIT_HLIST_NODE(&e->collision); e->epoch = NULL; @@ -1275,6 +1270,7 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __ data_size -= dgs; + ERR_IF(data_size == 0) return NULL; ERR_IF(data_size & 0x1ff) return NULL; ERR_IF(data_size > DRBD_MAX_BIO_SIZE) return NULL; @@ -1295,9 +1291,6 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __ if (!e) return NULL; - if (!data_size) - return e; - ds = data_size; page = e->pages; page_chain_for_each(page) { @@ -1722,10 +1715,6 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned dp_flags = be32_to_cpu(p->dp_flags); rw |= wire_flags_to_bio(mdev, dp_flags); - if (e->pages == NULL) { - D_ASSERT(e->size == 0); - D_ASSERT(dp_flags & DP_FLUSH); - } if (dp_flags & DP_MAY_SET_IN_SYNC) e->flags |= EE_MAY_SET_IN_SYNC; @@ -3812,18 +3801,11 @@ void drbd_free_tl_hash(struct drbd_conf *mdev) mdev->ee_hash = NULL; mdev->ee_hash_s = 0; - /* We may not have had the chance to wait for all locally pending - * application requests. The hlist_add_fake() prevents access after - * free on master bio completion. */ - for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++) { - struct drbd_request *req; - struct hlist_node *pos, *n; - hlist_for_each_entry_safe(req, pos, n, h, collision) { - hlist_del_init(&req->collision); - hlist_add_fake(&req->collision); - } - } - + /* paranoia code */ + for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++) + if (h->first) + dev_err(DEV, "ASSERT FAILED tl_hash[%u] == %p, expected NULL\n", + (int)(h - mdev->tl_hash), h->first); kfree(mdev->tl_hash); mdev->tl_hash = NULL; mdev->tl_hash_s = 0; diff --git a/trunk/drivers/block/drbd/drbd_req.c b/trunk/drivers/block/drbd/drbd_req.c index 910335c30927..8e93a6ac9bb6 100644 --- a/trunk/drivers/block/drbd/drbd_req.c +++ b/trunk/drivers/block/drbd/drbd_req.c @@ -455,7 +455,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what, req->rq_state |= RQ_LOCAL_COMPLETED; req->rq_state &= ~RQ_LOCAL_PENDING; - __drbd_chk_io_error(mdev, DRBD_IO_ERROR); + __drbd_chk_io_error(mdev, false); _req_may_be_done_not_susp(req, m); break; @@ -477,7 +477,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what, break; } - __drbd_chk_io_error(mdev, DRBD_IO_ERROR); + __drbd_chk_io_error(mdev, false); goto_queue_for_net_read: @@ -1111,12 +1111,13 @@ void drbd_make_request(struct request_queue *q, struct bio *bio) /* * what we "blindly" assume: */ + D_ASSERT(bio->bi_size > 0); D_ASSERT((bio->bi_size & 0x1ff) == 0); /* to make some things easier, force alignment of requests within the * granularity of our hash tables */ s_enr = bio->bi_sector >> HT_SHIFT; - e_enr = bio->bi_size ? (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT : s_enr; + e_enr = (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT; if (likely(s_enr == e_enr)) { do { @@ -1274,7 +1275,7 @@ void request_timer_fn(unsigned long data) time_after(now, req->start_time + dt) && !time_in_range(now, mdev->last_reattach_jif, mdev->last_reattach_jif + dt)) { dev_warn(DEV, "Local backing device failed to meet the disk-timeout\n"); - __drbd_chk_io_error(mdev, DRBD_FORCE_DETACH); + __drbd_chk_io_error(mdev, 1); } nt = (time_after(now, req->start_time + et) ? now : req->start_time) + et; spin_unlock_irq(&mdev->req_lock); diff --git a/trunk/drivers/block/drbd/drbd_worker.c b/trunk/drivers/block/drbd/drbd_worker.c index 6bce2cc179d4..620c70ff2231 100644 --- a/trunk/drivers/block/drbd/drbd_worker.c +++ b/trunk/drivers/block/drbd/drbd_worker.c @@ -111,7 +111,7 @@ void drbd_endio_read_sec_final(struct drbd_epoch_entry *e) __releases(local) if (list_empty(&mdev->read_ee)) wake_up(&mdev->ee_wait); if (test_bit(__EE_WAS_ERROR, &e->flags)) - __drbd_chk_io_error(mdev, DRBD_IO_ERROR); + __drbd_chk_io_error(mdev, false); spin_unlock_irqrestore(&mdev->req_lock, flags); drbd_queue_work(&mdev->data.work, &e->w); @@ -154,7 +154,7 @@ static void drbd_endio_write_sec_final(struct drbd_epoch_entry *e) __releases(lo : list_empty(&mdev->active_ee); if (test_bit(__EE_WAS_ERROR, &e->flags)) - __drbd_chk_io_error(mdev, DRBD_IO_ERROR); + __drbd_chk_io_error(mdev, false); spin_unlock_irqrestore(&mdev->req_lock, flags); if (is_syncer_req) @@ -1501,6 +1501,14 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side) return; } + if (mdev->state.conn < C_AHEAD) { + /* In case a previous resync run was aborted by an IO error/detach on the peer. */ + drbd_rs_cancel_all(mdev); + /* This should be done when we abort the resync. We definitely do not + want to have this for connections going back and forth between + Ahead/Behind and SyncSource/SyncTarget */ + } + if (side == C_SYNC_TARGET) { /* Since application IO was locked out during C_WF_BITMAP_T and C_WF_SYNC_UUID we are still unmodified. Before going to C_SYNC_TARGET diff --git a/trunk/drivers/block/floppy.c b/trunk/drivers/block/floppy.c index a7d6347aaa79..553f43a90953 100644 --- a/trunk/drivers/block/floppy.c +++ b/trunk/drivers/block/floppy.c @@ -191,7 +191,6 @@ static int print_unex = 1; #include #include #include -#include /* * PS/2 floppies have much slower step rates than regular floppies. @@ -2517,7 +2516,8 @@ static int make_raw_rw_request(void) set_fdc((long)current_req->rq_disk->private_data); raw_cmd = &default_raw_cmd; - raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK; + raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK | + FD_RAW_NEED_SEEK; raw_cmd->cmd_count = NR_RW; if (rq_data_dir(current_req) == READ) { raw_cmd->flags |= FD_RAW_READ; @@ -4123,7 +4123,7 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data) return get_disk(disks[drive]); } -static int __init do_floppy_init(void) +static int __init floppy_init(void) { int i, unit, drive; int err, dr; @@ -4338,24 +4338,6 @@ static int __init do_floppy_init(void) return err; } -#ifndef MODULE -static __init void floppy_async_init(void *data, async_cookie_t cookie) -{ - do_floppy_init(); -} -#endif - -static int __init floppy_init(void) -{ -#ifdef MODULE - return do_floppy_init(); -#else - /* Don't hold up the bootup by the floppy initialization */ - async_schedule(floppy_async_init, NULL); - return 0; -#endif -} - static const struct io_region { int offset; int size; diff --git a/trunk/drivers/block/nbd.c b/trunk/drivers/block/nbd.c index d07c9f7fded6..061427a75d37 100644 --- a/trunk/drivers/block/nbd.c +++ b/trunk/drivers/block/nbd.c @@ -154,7 +154,6 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size, struct msghdr msg; struct kvec iov; sigset_t blocked, oldset; - unsigned long pflags = current->flags; if (unlikely(!sock)) { dev_err(disk_to_dev(nbd->disk), @@ -168,9 +167,8 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size, siginitsetinv(&blocked, sigmask(SIGKILL)); sigprocmask(SIG_SETMASK, &blocked, &oldset); - current->flags |= PF_MEMALLOC; do { - sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC; + sock->sk->sk_allocation = GFP_NOIO; iov.iov_base = buf; iov.iov_len = size; msg.msg_name = NULL; @@ -216,7 +214,6 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size, } while (size > 0); sigprocmask(SIG_SETMASK, &oldset, NULL); - tsk_restore_flags(current, pflags, PF_MEMALLOC); return result; } @@ -408,7 +405,6 @@ static int nbd_do_it(struct nbd_device *nbd) BUG_ON(nbd->magic != NBD_MAGIC); - sk_set_memalloc(nbd->sock->sk); nbd->pid = task_pid_nr(current); ret = device_create_file(disk_to_dev(nbd->disk), &pid_attr); if (ret) { @@ -485,7 +481,7 @@ static void nbd_handle_req(struct nbd_device *nbd, struct request *req) nbd_end_request(req); } else { spin_lock(&nbd->queue_lock); - list_add_tail(&req->queuelist, &nbd->queue_head); + list_add(&req->queuelist, &nbd->queue_head); spin_unlock(&nbd->queue_lock); } diff --git a/trunk/drivers/block/rbd.c b/trunk/drivers/block/rbd.c index 9917943a3572..8f428a8ab003 100644 --- a/trunk/drivers/block/rbd.c +++ b/trunk/drivers/block/rbd.c @@ -55,6 +55,8 @@ #define RBD_MINORS_PER_MAJOR 256 /* max minors per blkdev */ +#define RBD_MAX_MD_NAME_LEN (RBD_MAX_OBJ_NAME_LEN + sizeof(RBD_SUFFIX)) +#define RBD_MAX_POOL_NAME_LEN 64 #define RBD_MAX_SNAP_NAME_LEN 32 #define RBD_MAX_OPT_LEN 1024 @@ -76,12 +78,13 @@ */ struct rbd_image_header { u64 image_size; - char *object_prefix; + char block_name[32]; __u8 obj_order; __u8 crypt_type; __u8 comp_type; struct ceph_snap_context *snapc; size_t snap_names_len; + u64 snap_seq; u32 total_snaps; char *snap_names; @@ -147,7 +150,7 @@ struct rbd_snap { * a single device */ struct rbd_device { - int dev_id; /* blkdev unique id */ + int id; /* blkdev unique id */ int major; /* blkdev assigned major */ struct gendisk *disk; /* blkdev's gendisk and rq */ @@ -160,24 +163,20 @@ struct rbd_device { spinlock_t lock; /* queue lock */ struct rbd_image_header header; - char *image_name; - size_t image_name_len; - char *header_name; - char *pool_name; - int pool_id; + char obj[RBD_MAX_OBJ_NAME_LEN]; /* rbd image name */ + int obj_len; + char obj_md_name[RBD_MAX_MD_NAME_LEN]; /* hdr nm. */ + char pool_name[RBD_MAX_POOL_NAME_LEN]; + int poolid; struct ceph_osd_event *watch_event; struct ceph_osd_request *watch_request; /* protects updating the header */ struct rw_semaphore header_rwsem; - /* name of the snapshot this device reads from */ - char *snap_name; - /* id of the snapshot this device reads from */ + char snap_name[RBD_MAX_SNAP_NAME_LEN]; u64 snap_id; /* current snapshot id */ - /* whether the snap_id this device reads from still exists */ - bool snap_exists; - int read_only; + int read_only; struct list_head node; @@ -202,7 +201,8 @@ static ssize_t rbd_snap_add(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); -static void __rbd_remove_snap_dev(struct rbd_snap *snap); +static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev, + struct rbd_snap *snap); static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count); @@ -240,7 +240,7 @@ static void rbd_put_dev(struct rbd_device *rbd_dev) put_device(&rbd_dev->dev); } -static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver); +static int __rbd_refresh_header(struct rbd_device *rbd_dev); static int rbd_open(struct block_device *bdev, fmode_t mode) { @@ -273,9 +273,9 @@ static const struct block_device_operations rbd_bd_ops = { /* * Initialize an rbd client instance. - * We own *ceph_opts. + * We own *opt. */ -static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts, +static struct rbd_client *rbd_client_create(struct ceph_options *opt, struct rbd_options *rbd_opts) { struct rbd_client *rbdc; @@ -291,10 +291,10 @@ static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts, mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rbdc->client = ceph_create_client(ceph_opts, rbdc, 0, 0); + rbdc->client = ceph_create_client(opt, rbdc, 0, 0); if (IS_ERR(rbdc->client)) goto out_mutex; - ceph_opts = NULL; /* Now rbdc->client is responsible for ceph_opts */ + opt = NULL; /* Now rbdc->client is responsible for opt */ ret = ceph_open_session(rbdc->client); if (ret < 0) @@ -317,23 +317,23 @@ static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts, mutex_unlock(&ctl_mutex); kfree(rbdc); out_opt: - if (ceph_opts) - ceph_destroy_options(ceph_opts); + if (opt) + ceph_destroy_options(opt); return ERR_PTR(ret); } /* * Find a ceph client with specific addr and configuration. */ -static struct rbd_client *__rbd_client_find(struct ceph_options *ceph_opts) +static struct rbd_client *__rbd_client_find(struct ceph_options *opt) { struct rbd_client *client_node; - if (ceph_opts->flags & CEPH_OPT_NOSHARE) + if (opt->flags & CEPH_OPT_NOSHARE) return NULL; list_for_each_entry(client_node, &rbd_client_list, node) - if (!ceph_compare_options(ceph_opts, client_node->client)) + if (ceph_compare_options(opt, client_node->client) == 0) return client_node; return NULL; } @@ -349,7 +349,7 @@ enum { /* string args above */ }; -static match_table_t rbd_opts_tokens = { +static match_table_t rbdopt_tokens = { {Opt_notify_timeout, "notify_timeout=%d"}, /* int args above */ /* string args above */ @@ -358,11 +358,11 @@ static match_table_t rbd_opts_tokens = { static int parse_rbd_opts_token(char *c, void *private) { - struct rbd_options *rbd_opts = private; + struct rbd_options *rbdopt = private; substring_t argstr[MAX_OPT_ARGS]; int token, intval, ret; - token = match_token(c, rbd_opts_tokens, argstr); + token = match_token(c, rbdopt_tokens, argstr); if (token < 0) return -EINVAL; @@ -383,7 +383,7 @@ static int parse_rbd_opts_token(char *c, void *private) switch (token) { case Opt_notify_timeout: - rbd_opts->notify_timeout = intval; + rbdopt->notify_timeout = intval; break; default: BUG_ON(token); @@ -400,7 +400,7 @@ static struct rbd_client *rbd_get_client(const char *mon_addr, char *options) { struct rbd_client *rbdc; - struct ceph_options *ceph_opts; + struct ceph_options *opt; struct rbd_options *rbd_opts; rbd_opts = kzalloc(sizeof(*rbd_opts), GFP_KERNEL); @@ -409,29 +409,29 @@ static struct rbd_client *rbd_get_client(const char *mon_addr, rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT; - ceph_opts = ceph_parse_options(options, mon_addr, - mon_addr + mon_addr_len, - parse_rbd_opts_token, rbd_opts); - if (IS_ERR(ceph_opts)) { + opt = ceph_parse_options(options, mon_addr, + mon_addr + mon_addr_len, + parse_rbd_opts_token, rbd_opts); + if (IS_ERR(opt)) { kfree(rbd_opts); - return ERR_CAST(ceph_opts); + return ERR_CAST(opt); } spin_lock(&rbd_client_list_lock); - rbdc = __rbd_client_find(ceph_opts); + rbdc = __rbd_client_find(opt); if (rbdc) { /* using an existing client */ kref_get(&rbdc->kref); spin_unlock(&rbd_client_list_lock); - ceph_destroy_options(ceph_opts); + ceph_destroy_options(opt); kfree(rbd_opts); return rbdc; } spin_unlock(&rbd_client_list_lock); - rbdc = rbd_client_create(ceph_opts, rbd_opts); + rbdc = rbd_client_create(opt, rbd_opts); if (IS_ERR(rbdc)) kfree(rbd_opts); @@ -480,60 +480,46 @@ static void rbd_coll_release(struct kref *kref) kfree(coll); } -static bool rbd_dev_ondisk_valid(struct rbd_image_header_ondisk *ondisk) -{ - return !memcmp(&ondisk->text, - RBD_HEADER_TEXT, sizeof (RBD_HEADER_TEXT)); -} - /* * Create a new header structure, translate header format from the on-disk * header. */ static int rbd_header_from_disk(struct rbd_image_header *header, struct rbd_image_header_ondisk *ondisk, - u32 allocated_snaps) + u32 allocated_snaps, + gfp_t gfp_flags) { - u32 snap_count; + u32 i, snap_count; - if (!rbd_dev_ondisk_valid(ondisk)) + if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT))) return -ENXIO; snap_count = le32_to_cpu(ondisk->snap_count); - if (snap_count > (SIZE_MAX - sizeof(struct ceph_snap_context)) - / sizeof (u64)) + if (snap_count > (UINT_MAX - sizeof(struct ceph_snap_context)) + / sizeof (*ondisk)) return -EINVAL; header->snapc = kmalloc(sizeof(struct ceph_snap_context) + snap_count * sizeof(u64), - GFP_KERNEL); + gfp_flags); if (!header->snapc) return -ENOMEM; + header->snap_names_len = le64_to_cpu(ondisk->snap_names_len); if (snap_count) { - header->snap_names_len = le64_to_cpu(ondisk->snap_names_len); header->snap_names = kmalloc(header->snap_names_len, - GFP_KERNEL); + gfp_flags); if (!header->snap_names) goto err_snapc; header->snap_sizes = kmalloc(snap_count * sizeof(u64), - GFP_KERNEL); + gfp_flags); if (!header->snap_sizes) goto err_names; } else { - WARN_ON(ondisk->snap_names_len); - header->snap_names_len = 0; header->snap_names = NULL; header->snap_sizes = NULL; } - - header->object_prefix = kmalloc(sizeof (ondisk->block_name) + 1, - GFP_KERNEL); - if (!header->object_prefix) - goto err_sizes; - - memcpy(header->object_prefix, ondisk->block_name, + memcpy(header->block_name, ondisk->block_name, sizeof(ondisk->block_name)); - header->object_prefix[sizeof (ondisk->block_name)] = '\0'; header->image_size = le64_to_cpu(ondisk->image_size); header->obj_order = ondisk->options.order; @@ -541,13 +527,11 @@ static int rbd_header_from_disk(struct rbd_image_header *header, header->comp_type = ondisk->options.comp_type; atomic_set(&header->snapc->nref, 1); - header->snapc->seq = le64_to_cpu(ondisk->snap_seq); + header->snap_seq = le64_to_cpu(ondisk->snap_seq); header->snapc->num_snaps = snap_count; header->total_snaps = snap_count; if (snap_count && allocated_snaps == snap_count) { - int i; - for (i = 0; i < snap_count; i++) { header->snapc->snaps[i] = le64_to_cpu(ondisk->snaps[i].id); @@ -556,22 +540,16 @@ static int rbd_header_from_disk(struct rbd_image_header *header, } /* copy snapshot names */ - memcpy(header->snap_names, &ondisk->snaps[snap_count], + memcpy(header->snap_names, &ondisk->snaps[i], header->snap_names_len); } return 0; -err_sizes: - kfree(header->snap_sizes); - header->snap_sizes = NULL; err_names: kfree(header->snap_names); - header->snap_names = NULL; err_snapc: kfree(header->snapc); - header->snapc = NULL; - return -ENOMEM; } @@ -597,50 +575,52 @@ static int snap_by_name(struct rbd_image_header *header, const char *snap_name, return -ENOENT; } -static int rbd_header_set_snap(struct rbd_device *rbd_dev, u64 *size) +static int rbd_header_set_snap(struct rbd_device *dev, u64 *size) { - int ret; + struct rbd_image_header *header = &dev->header; + struct ceph_snap_context *snapc = header->snapc; + int ret = -ENOENT; - down_write(&rbd_dev->header_rwsem); + BUILD_BUG_ON(sizeof (dev->snap_name) < sizeof (RBD_SNAP_HEAD_NAME)); + + down_write(&dev->header_rwsem); - if (!memcmp(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME, + if (!memcmp(dev->snap_name, RBD_SNAP_HEAD_NAME, sizeof (RBD_SNAP_HEAD_NAME))) { - rbd_dev->snap_id = CEPH_NOSNAP; - rbd_dev->snap_exists = false; - rbd_dev->read_only = 0; + if (header->total_snaps) + snapc->seq = header->snap_seq; + else + snapc->seq = 0; + dev->snap_id = CEPH_NOSNAP; + dev->read_only = 0; if (size) - *size = rbd_dev->header.image_size; + *size = header->image_size; } else { - u64 snap_id = 0; - - ret = snap_by_name(&rbd_dev->header, rbd_dev->snap_name, - &snap_id, size); + ret = snap_by_name(header, dev->snap_name, &snapc->seq, size); if (ret < 0) goto done; - rbd_dev->snap_id = snap_id; - rbd_dev->snap_exists = true; - rbd_dev->read_only = 1; + dev->snap_id = snapc->seq; + dev->read_only = 1; } ret = 0; done: - up_write(&rbd_dev->header_rwsem); + up_write(&dev->header_rwsem); return ret; } static void rbd_header_free(struct rbd_image_header *header) { - kfree(header->object_prefix); - kfree(header->snap_sizes); + kfree(header->snapc); kfree(header->snap_names); - ceph_put_snap_context(header->snapc); + kfree(header->snap_sizes); } /* * get the actual striped segment name, offset and length */ static u64 rbd_get_segment(struct rbd_image_header *header, - const char *object_prefix, + const char *block_name, u64 ofs, u64 len, char *seg_name, u64 *segofs) { @@ -648,7 +628,7 @@ static u64 rbd_get_segment(struct rbd_image_header *header, if (seg_name) snprintf(seg_name, RBD_MAX_SEG_NAME_LEN, - "%s.%012llx", object_prefix, seg); + "%s.%012llx", block_name, seg); ofs = ofs & ((1 << header->obj_order) - 1); len = min_t(u64, len, (1 << header->obj_order) - ofs); @@ -746,8 +726,9 @@ static struct bio *bio_chain_clone(struct bio **old, struct bio **next, * split_bio will BUG_ON if this is not the case */ dout("bio_chain_clone split! total=%d remaining=%d" - "bi_size=%u\n", - total, len - total, old_chain->bi_size); + "bi_size=%d\n", + (int)total, (int)len-total, + (int)old_chain->bi_size); /* split the bio. We'll release it either in the next call, or it will have to be released outside */ @@ -796,24 +777,22 @@ static struct bio *bio_chain_clone(struct bio **old, struct bio **next, /* * helpers for osd request op vectors. */ -static struct ceph_osd_req_op *rbd_create_rw_ops(int num_ops, - int opcode, u32 payload_len) -{ - struct ceph_osd_req_op *ops; - - ops = kzalloc(sizeof (*ops) * (num_ops + 1), GFP_NOIO); - if (!ops) - return NULL; - - ops[0].op = opcode; - +static int rbd_create_rw_ops(struct ceph_osd_req_op **ops, + int num_ops, + int opcode, + u32 payload_len) +{ + *ops = kzalloc(sizeof(struct ceph_osd_req_op) * (num_ops + 1), + GFP_NOIO); + if (!*ops) + return -ENOMEM; + (*ops)[0].op = opcode; /* * op extent offset and length will be set later on * in calc_raw_layout() */ - ops[0].payload_len = payload_len; - - return ops; + (*ops)[0].payload_len = payload_len; + return 0; } static void rbd_destroy_ops(struct ceph_osd_req_op *ops) @@ -829,8 +808,8 @@ static void rbd_coll_end_req_index(struct request *rq, struct request_queue *q; int min, max, i; - dout("rbd_coll_end_req_index %p index %d ret %d len %llu\n", - coll, index, ret, (unsigned long long) len); + dout("rbd_coll_end_req_index %p index %d ret %d len %lld\n", + coll, index, ret, len); if (!rq) return; @@ -869,15 +848,16 @@ static void rbd_coll_end_req(struct rbd_request *req, * Send ceph osd request */ static int rbd_do_request(struct request *rq, - struct rbd_device *rbd_dev, + struct rbd_device *dev, struct ceph_snap_context *snapc, u64 snapid, - const char *object_name, u64 ofs, u64 len, + const char *obj, u64 ofs, u64 len, struct bio *bio, struct page **pages, int num_pages, int flags, struct ceph_osd_req_op *ops, + int num_reply, struct rbd_req_coll *coll, int coll_index, void (*rbd_cb)(struct ceph_osd_request *req, @@ -907,13 +887,15 @@ static int rbd_do_request(struct request *rq, req_data->coll_index = coll_index; } - dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", object_name, - (unsigned long long) ofs, (unsigned long long) len); + dout("rbd_do_request obj=%s ofs=%lld len=%lld\n", obj, len, ofs); - osdc = &rbd_dev->rbd_client->client->osdc; + down_read(&dev->header_rwsem); + + osdc = &dev->rbd_client->client->osdc; req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, false, GFP_NOIO, pages, bio); if (!req) { + up_read(&dev->header_rwsem); ret = -ENOMEM; goto done_pages; } @@ -930,7 +912,7 @@ static int rbd_do_request(struct request *rq, reqhead = req->r_request->front.iov_base; reqhead->snapid = cpu_to_le64(CEPH_NOSNAP); - strncpy(req->r_oid, object_name, sizeof(req->r_oid)); + strncpy(req->r_oid, obj, sizeof(req->r_oid)); req->r_oid_len = strlen(req->r_oid); layout = &req->r_file_layout; @@ -938,7 +920,7 @@ static int rbd_do_request(struct request *rq, layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); layout->fl_stripe_count = cpu_to_le32(1); layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_pg_pool = cpu_to_le32(rbd_dev->pool_id); + layout->fl_pg_pool = cpu_to_le32(dev->poolid); ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno, req, ops); @@ -947,6 +929,7 @@ static int rbd_do_request(struct request *rq, snapc, &mtime, req->r_oid, req->r_oid_len); + up_read(&dev->header_rwsem); if (linger_req) { ceph_osdc_set_request_linger(osdc, req); @@ -961,9 +944,8 @@ static int rbd_do_request(struct request *rq, ret = ceph_osdc_wait_request(osdc, req); if (ver) *ver = le64_to_cpu(req->r_reassert_version.version); - dout("reassert_ver=%llu\n", - (unsigned long long) - le64_to_cpu(req->r_reassert_version.version)); + dout("reassert_ver=%lld\n", + le64_to_cpu(req->r_reassert_version.version)); ceph_osdc_put_request(req); } return ret; @@ -997,8 +979,7 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) bytes = le64_to_cpu(op->extent.length); read_op = (le16_to_cpu(op->op) == CEPH_OSD_OP_READ); - dout("rbd_req_cb bytes=%llu readop=%d rc=%d\n", - (unsigned long long) bytes, read_op, (int) rc); + dout("rbd_req_cb bytes=%lld readop=%d rc=%d\n", bytes, read_op, rc); if (rc == -ENOENT && read_op) { zero_bio_chain(req_data->bio, 0); @@ -1025,12 +1006,14 @@ static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg /* * Do a synchronous ceph osd operation */ -static int rbd_req_sync_op(struct rbd_device *rbd_dev, +static int rbd_req_sync_op(struct rbd_device *dev, struct ceph_snap_context *snapc, u64 snapid, + int opcode, int flags, - struct ceph_osd_req_op *ops, - const char *object_name, + struct ceph_osd_req_op *orig_ops, + int num_reply, + const char *obj, u64 ofs, u64 len, char *buf, struct ceph_osd_request **linger_req, @@ -1039,28 +1022,45 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, int ret; struct page **pages; int num_pages; - - BUG_ON(ops == NULL); + struct ceph_osd_req_op *ops = orig_ops; + u32 payload_len; num_pages = calc_pages_for(ofs , len); pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); if (IS_ERR(pages)) return PTR_ERR(pages); - ret = rbd_do_request(NULL, rbd_dev, snapc, snapid, - object_name, ofs, len, NULL, + if (!orig_ops) { + payload_len = (flags & CEPH_OSD_FLAG_WRITE ? len : 0); + ret = rbd_create_rw_ops(&ops, 1, opcode, payload_len); + if (ret < 0) + goto done; + + if ((flags & CEPH_OSD_FLAG_WRITE) && buf) { + ret = ceph_copy_to_page_vector(pages, buf, ofs, len); + if (ret < 0) + goto done_ops; + } + } + + ret = rbd_do_request(NULL, dev, snapc, snapid, + obj, ofs, len, NULL, pages, num_pages, flags, ops, + 2, NULL, 0, NULL, linger_req, ver); if (ret < 0) - goto done; + goto done_ops; if ((flags & CEPH_OSD_FLAG_READ) && buf) ret = ceph_copy_from_page_vector(pages, buf, ofs, ret); +done_ops: + if (!orig_ops) + rbd_destroy_ops(ops); done: ceph_release_page_vector(pages, num_pages); return ret; @@ -1070,10 +1070,10 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, * Do an asynchronous ceph osd operation */ static int rbd_do_op(struct request *rq, - struct rbd_device *rbd_dev, + struct rbd_device *rbd_dev , struct ceph_snap_context *snapc, u64 snapid, - int opcode, int flags, + int opcode, int flags, int num_reply, u64 ofs, u64 len, struct bio *bio, struct rbd_req_coll *coll, @@ -1091,15 +1091,14 @@ static int rbd_do_op(struct request *rq, return -ENOMEM; seg_len = rbd_get_segment(&rbd_dev->header, - rbd_dev->header.object_prefix, + rbd_dev->header.block_name, ofs, len, seg_name, &seg_ofs); payload_len = (flags & CEPH_OSD_FLAG_WRITE ? seg_len : 0); - ret = -ENOMEM; - ops = rbd_create_rw_ops(1, opcode, payload_len); - if (!ops) + ret = rbd_create_rw_ops(&ops, 1, opcode, payload_len); + if (ret < 0) goto done; /* we've taken care of segment sizes earlier when we @@ -1113,6 +1112,7 @@ static int rbd_do_op(struct request *rq, NULL, 0, flags, ops, + num_reply, coll, coll_index, rbd_req_cb, 0, NULL); @@ -1136,6 +1136,7 @@ static int rbd_req_write(struct request *rq, return rbd_do_op(rq, rbd_dev, snapc, CEPH_NOSNAP, CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, + 2, ofs, len, bio, coll, coll_index); } @@ -1154,58 +1155,55 @@ static int rbd_req_read(struct request *rq, snapid, CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, + 2, ofs, len, bio, coll, coll_index); } /* * Request sync osd read */ -static int rbd_req_sync_read(struct rbd_device *rbd_dev, +static int rbd_req_sync_read(struct rbd_device *dev, + struct ceph_snap_context *snapc, u64 snapid, - const char *object_name, + const char *obj, u64 ofs, u64 len, char *buf, u64 *ver) { - struct ceph_osd_req_op *ops; - int ret; - - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_READ, 0); - if (!ops) - return -ENOMEM; - - ret = rbd_req_sync_op(rbd_dev, NULL, + return rbd_req_sync_op(dev, NULL, snapid, + CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, - ops, object_name, ofs, len, buf, NULL, ver); - rbd_destroy_ops(ops); - - return ret; + NULL, + 1, obj, ofs, len, buf, NULL, ver); } /* * Request sync osd watch */ -static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, +static int rbd_req_sync_notify_ack(struct rbd_device *dev, u64 ver, - u64 notify_id) + u64 notify_id, + const char *obj) { struct ceph_osd_req_op *ops; + struct page **pages = NULL; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY_ACK, 0); - if (!ops) - return -ENOMEM; + ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY_ACK, 0); + if (ret < 0) + return ret; - ops[0].watch.ver = cpu_to_le64(ver); + ops[0].watch.ver = cpu_to_le64(dev->header.obj_version); ops[0].watch.cookie = notify_id; ops[0].watch.flag = 0; - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, - rbd_dev->header_name, 0, 0, NULL, - NULL, 0, + ret = rbd_do_request(NULL, dev, NULL, CEPH_NOSNAP, + obj, 0, 0, NULL, + pages, 0, CEPH_OSD_FLAG_READ, ops, + 1, NULL, 0, rbd_simple_req_cb, 0, NULL); @@ -1215,53 +1213,54 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) { - struct rbd_device *rbd_dev = (struct rbd_device *)data; - u64 hver; + struct rbd_device *dev = (struct rbd_device *)data; int rc; - if (!rbd_dev) + if (!dev) return; - dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n", - rbd_dev->header_name, (unsigned long long) notify_id, - (unsigned int) opcode); - rc = rbd_refresh_header(rbd_dev, &hver); + dout("rbd_watch_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name, + notify_id, (int)opcode); + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + rc = __rbd_refresh_header(dev); + mutex_unlock(&ctl_mutex); if (rc) pr_warning(RBD_DRV_NAME "%d got notification but failed to " - " update snaps: %d\n", rbd_dev->major, rc); + " update snaps: %d\n", dev->major, rc); - rbd_req_sync_notify_ack(rbd_dev, hver, notify_id); + rbd_req_sync_notify_ack(dev, ver, notify_id, dev->obj_md_name); } /* * Request sync osd watch */ -static int rbd_req_sync_watch(struct rbd_device *rbd_dev) +static int rbd_req_sync_watch(struct rbd_device *dev, + const char *obj, + u64 ver) { struct ceph_osd_req_op *ops; - struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; - int ret; + struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0); - if (!ops) - return -ENOMEM; + int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0); + if (ret < 0) + return ret; ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, - (void *)rbd_dev, &rbd_dev->watch_event); + (void *)dev, &dev->watch_event); if (ret < 0) goto fail; - ops[0].watch.ver = cpu_to_le64(rbd_dev->header.obj_version); - ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); + ops[0].watch.ver = cpu_to_le64(ver); + ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie); ops[0].watch.flag = 1; - ret = rbd_req_sync_op(rbd_dev, NULL, + ret = rbd_req_sync_op(dev, NULL, CEPH_NOSNAP, + 0, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, - rbd_dev->header_name, - 0, 0, NULL, - &rbd_dev->watch_request, NULL); + 1, obj, 0, 0, NULL, + &dev->watch_request, NULL); if (ret < 0) goto fail_event; @@ -1270,8 +1269,8 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) return 0; fail_event: - ceph_osdc_cancel_event(rbd_dev->watch_event); - rbd_dev->watch_event = NULL; + ceph_osdc_cancel_event(dev->watch_event); + dev->watch_event = NULL; fail: rbd_destroy_ops(ops); return ret; @@ -1280,65 +1279,64 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) /* * Request sync osd unwatch */ -static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) +static int rbd_req_sync_unwatch(struct rbd_device *dev, + const char *obj) { struct ceph_osd_req_op *ops; - int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0); - if (!ops) - return -ENOMEM; + int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0); + if (ret < 0) + return ret; ops[0].watch.ver = 0; - ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); + ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie); ops[0].watch.flag = 0; - ret = rbd_req_sync_op(rbd_dev, NULL, + ret = rbd_req_sync_op(dev, NULL, CEPH_NOSNAP, + 0, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, - rbd_dev->header_name, - 0, 0, NULL, NULL, NULL); - + 1, obj, 0, 0, NULL, NULL, NULL); rbd_destroy_ops(ops); - ceph_osdc_cancel_event(rbd_dev->watch_event); - rbd_dev->watch_event = NULL; + ceph_osdc_cancel_event(dev->watch_event); + dev->watch_event = NULL; return ret; } struct rbd_notify_info { - struct rbd_device *rbd_dev; + struct rbd_device *dev; }; static void rbd_notify_cb(u64 ver, u64 notify_id, u8 opcode, void *data) { - struct rbd_device *rbd_dev = (struct rbd_device *)data; - if (!rbd_dev) + struct rbd_device *dev = (struct rbd_device *)data; + if (!dev) return; - dout("rbd_notify_cb %s notify_id=%llu opcode=%u\n", - rbd_dev->header_name, (unsigned long long) notify_id, - (unsigned int) opcode); + dout("rbd_notify_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name, + notify_id, (int)opcode); } /* * Request sync osd notify */ -static int rbd_req_sync_notify(struct rbd_device *rbd_dev) +static int rbd_req_sync_notify(struct rbd_device *dev, + const char *obj) { struct ceph_osd_req_op *ops; - struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc; struct ceph_osd_event *event; struct rbd_notify_info info; int payload_len = sizeof(u32) + sizeof(u32); int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY, payload_len); - if (!ops) - return -ENOMEM; + ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY, payload_len); + if (ret < 0) + return ret; - info.rbd_dev = rbd_dev; + info.dev = dev; ret = ceph_osdc_create_event(osdc, rbd_notify_cb, 1, (void *)&info, &event); @@ -1351,12 +1349,12 @@ static int rbd_req_sync_notify(struct rbd_device *rbd_dev) ops[0].watch.prot_ver = RADOS_NOTIFY_VER; ops[0].watch.timeout = 12; - ret = rbd_req_sync_op(rbd_dev, NULL, + ret = rbd_req_sync_op(dev, NULL, CEPH_NOSNAP, + 0, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, - rbd_dev->header_name, - 0, 0, NULL, NULL, NULL); + 1, obj, 0, 0, NULL, NULL, NULL); if (ret < 0) goto fail_event; @@ -1375,37 +1373,36 @@ static int rbd_req_sync_notify(struct rbd_device *rbd_dev) /* * Request sync osd read */ -static int rbd_req_sync_exec(struct rbd_device *rbd_dev, - const char *object_name, - const char *class_name, - const char *method_name, +static int rbd_req_sync_exec(struct rbd_device *dev, + const char *obj, + const char *cls, + const char *method, const char *data, int len, u64 *ver) { struct ceph_osd_req_op *ops; - int class_name_len = strlen(class_name); - int method_name_len = strlen(method_name); - int ret; - - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_CALL, - class_name_len + method_name_len + len); - if (!ops) - return -ENOMEM; + int cls_len = strlen(cls); + int method_len = strlen(method); + int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_CALL, + cls_len + method_len + len); + if (ret < 0) + return ret; - ops[0].cls.class_name = class_name; - ops[0].cls.class_len = (__u8) class_name_len; - ops[0].cls.method_name = method_name; - ops[0].cls.method_len = (__u8) method_name_len; + ops[0].cls.class_name = cls; + ops[0].cls.class_len = (__u8)cls_len; + ops[0].cls.method_name = method; + ops[0].cls.method_len = (__u8)method_len; ops[0].cls.argc = 0; ops[0].cls.indata = data; ops[0].cls.indata_len = len; - ret = rbd_req_sync_op(rbd_dev, NULL, + ret = rbd_req_sync_op(dev, NULL, CEPH_NOSNAP, + 0, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, - object_name, 0, 0, NULL, NULL, ver); + 1, obj, 0, 0, NULL, NULL, ver); rbd_destroy_ops(ops); @@ -1440,12 +1437,10 @@ static void rbd_rq_fn(struct request_queue *q) struct bio *bio; struct bio *rq_bio, *next_bio = NULL; bool do_write; - unsigned int size; - u64 op_size = 0; + int size, op_size = 0; u64 ofs; int num_segs, cur_seg = 0; struct rbd_req_coll *coll; - struct ceph_snap_context *snapc; /* peek at request from block layer */ if (!rq) @@ -1472,38 +1467,23 @@ static void rbd_rq_fn(struct request_queue *q) spin_unlock_irq(q->queue_lock); - down_read(&rbd_dev->header_rwsem); - - if (rbd_dev->snap_id != CEPH_NOSNAP && !rbd_dev->snap_exists) { - up_read(&rbd_dev->header_rwsem); - dout("request for non-existent snapshot"); - spin_lock_irq(q->queue_lock); - __blk_end_request_all(rq, -ENXIO); - continue; - } - - snapc = ceph_get_snap_context(rbd_dev->header.snapc); - - up_read(&rbd_dev->header_rwsem); - dout("%s 0x%x bytes at 0x%llx\n", do_write ? "write" : "read", - size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); + size, blk_rq_pos(rq) * SECTOR_SIZE); num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); coll = rbd_alloc_coll(num_segs); if (!coll) { spin_lock_irq(q->queue_lock); __blk_end_request_all(rq, -ENOMEM); - ceph_put_snap_context(snapc); continue; } do { /* a bio clone to be passed down to OSD req */ - dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt); + dout("rq->bio->bi_vcnt=%d\n", rq->bio->bi_vcnt); op_size = rbd_get_segment(&rbd_dev->header, - rbd_dev->header.object_prefix, + rbd_dev->header.block_name, ofs, size, NULL, NULL); kref_get(&coll->kref); @@ -1519,7 +1499,7 @@ static void rbd_rq_fn(struct request_queue *q) /* init OSD command: write or read */ if (do_write) rbd_req_write(rq, rbd_dev, - snapc, + rbd_dev->header.snapc, ofs, op_size, bio, coll, cur_seg); @@ -1542,8 +1522,6 @@ static void rbd_rq_fn(struct request_queue *q) if (bp) bio_pair_release(bp); spin_lock_irq(q->queue_lock); - - ceph_put_snap_context(snapc); } } @@ -1614,19 +1592,18 @@ static int rbd_read_header(struct rbd_device *rbd_dev, return -ENOMEM; rc = rbd_req_sync_read(rbd_dev, - CEPH_NOSNAP, - rbd_dev->header_name, + NULL, CEPH_NOSNAP, + rbd_dev->obj_md_name, 0, len, (char *)dh, &ver); if (rc < 0) goto out_dh; - rc = rbd_header_from_disk(header, dh, snap_count); + rc = rbd_header_from_disk(header, dh, snap_count, GFP_KERNEL); if (rc < 0) { if (rc == -ENXIO) pr_warning("unrecognized header format" - " for image %s\n", - rbd_dev->image_name); + " for image %s", rbd_dev->obj); goto out_dh; } @@ -1651,7 +1628,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev, /* * create a snapshot */ -static int rbd_header_add_snap(struct rbd_device *rbd_dev, +static int rbd_header_add_snap(struct rbd_device *dev, const char *snap_name, gfp_t gfp_flags) { @@ -1659,15 +1636,16 @@ static int rbd_header_add_snap(struct rbd_device *rbd_dev, u64 new_snapid; int ret; void *data, *p, *e; + u64 ver; struct ceph_mon_client *monc; /* we should create a snapshot only if we're pointing at the head */ - if (rbd_dev->snap_id != CEPH_NOSNAP) + if (dev->snap_id != CEPH_NOSNAP) return -EINVAL; - monc = &rbd_dev->rbd_client->client->monc; - ret = ceph_monc_create_snapid(monc, rbd_dev->pool_id, &new_snapid); - dout("created snapid=%llu\n", (unsigned long long) new_snapid); + monc = &dev->rbd_client->client->monc; + ret = ceph_monc_create_snapid(monc, dev->poolid, &new_snapid); + dout("created snapid=%lld\n", new_snapid); if (ret < 0) return ret; @@ -1681,13 +1659,19 @@ static int rbd_header_add_snap(struct rbd_device *rbd_dev, ceph_encode_string_safe(&p, e, snap_name, name_len, bad); ceph_encode_64_safe(&p, e, new_snapid, bad); - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, - "rbd", "snap_add", - data, p - data, NULL); + ret = rbd_req_sync_exec(dev, dev->obj_md_name, "rbd", "snap_add", + data, p - data, &ver); kfree(data); - return ret < 0 ? ret : 0; + if (ret < 0) + return ret; + + down_write(&dev->header_rwsem); + dev->header.snapc->seq = new_snapid; + up_write(&dev->header_rwsem); + + return 0; bad: return -ERANGE; } @@ -1695,52 +1679,52 @@ static int rbd_header_add_snap(struct rbd_device *rbd_dev, static void __rbd_remove_all_snaps(struct rbd_device *rbd_dev) { struct rbd_snap *snap; - struct rbd_snap *next; - list_for_each_entry_safe(snap, next, &rbd_dev->snaps, node) - __rbd_remove_snap_dev(snap); + while (!list_empty(&rbd_dev->snaps)) { + snap = list_first_entry(&rbd_dev->snaps, struct rbd_snap, node); + __rbd_remove_snap_dev(rbd_dev, snap); + } } /* * only read the first part of the ondisk header, without the snaps info */ -static int __rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver) +static int __rbd_refresh_header(struct rbd_device *rbd_dev) { int ret; struct rbd_image_header h; + u64 snap_seq; + int follow_seq = 0; ret = rbd_read_header(rbd_dev, &h); if (ret < 0) return ret; - down_write(&rbd_dev->header_rwsem); - /* resized? */ - if (rbd_dev->snap_id == CEPH_NOSNAP) { - sector_t size = (sector_t) h.image_size / SECTOR_SIZE; + set_capacity(rbd_dev->disk, h.image_size / SECTOR_SIZE); - dout("setting size to %llu sectors", (unsigned long long) size); - set_capacity(rbd_dev->disk, size); - } + down_write(&rbd_dev->header_rwsem); - /* rbd_dev->header.object_prefix shouldn't change */ - kfree(rbd_dev->header.snap_sizes); + snap_seq = rbd_dev->header.snapc->seq; + if (rbd_dev->header.total_snaps && + rbd_dev->header.snapc->snaps[0] == snap_seq) + /* pointing at the head, will need to follow that + if head moves */ + follow_seq = 1; + + kfree(rbd_dev->header.snapc); kfree(rbd_dev->header.snap_names); - /* osd requests may still refer to snapc */ - ceph_put_snap_context(rbd_dev->header.snapc); + kfree(rbd_dev->header.snap_sizes); - if (hver) - *hver = h.obj_version; - rbd_dev->header.obj_version = h.obj_version; - rbd_dev->header.image_size = h.image_size; rbd_dev->header.total_snaps = h.total_snaps; rbd_dev->header.snapc = h.snapc; rbd_dev->header.snap_names = h.snap_names; rbd_dev->header.snap_names_len = h.snap_names_len; rbd_dev->header.snap_sizes = h.snap_sizes; - /* Free the extra copy of the object prefix */ - WARN_ON(strcmp(rbd_dev->header.object_prefix, h.object_prefix)); - kfree(h.object_prefix); + if (follow_seq) + rbd_dev->header.snapc->seq = rbd_dev->header.snapc->snaps[0]; + else + rbd_dev->header.snapc->seq = snap_seq; ret = __rbd_init_snaps_header(rbd_dev); @@ -1749,17 +1733,6 @@ static int __rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver) return ret; } -static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver) -{ - int ret; - - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - ret = __rbd_refresh_header(rbd_dev, hver); - mutex_unlock(&ctl_mutex); - - return ret; -} - static int rbd_init_disk(struct rbd_device *rbd_dev) { struct gendisk *disk; @@ -1789,7 +1762,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) goto out; snprintf(disk->disk_name, sizeof(disk->disk_name), RBD_DRV_NAME "%d", - rbd_dev->dev_id); + rbd_dev->id); disk->major = rbd_dev->major; disk->first_minor = 0; disk->fops = &rbd_bd_ops; @@ -1846,13 +1819,8 @@ static ssize_t rbd_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - sector_t size; - - down_read(&rbd_dev->header_rwsem); - size = get_capacity(rbd_dev->disk); - up_read(&rbd_dev->header_rwsem); - return sprintf(buf, "%llu\n", (unsigned long long) size * SECTOR_SIZE); + return sprintf(buf, "%llu\n", (unsigned long long)rbd_dev->header.image_size); } static ssize_t rbd_major_show(struct device *dev, @@ -1880,20 +1848,12 @@ static ssize_t rbd_pool_show(struct device *dev, return sprintf(buf, "%s\n", rbd_dev->pool_name); } -static ssize_t rbd_pool_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - - return sprintf(buf, "%d\n", rbd_dev->pool_id); -} - static ssize_t rbd_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - return sprintf(buf, "%s\n", rbd_dev->image_name); + return sprintf(buf, "%s\n", rbd_dev->obj); } static ssize_t rbd_snap_show(struct device *dev, @@ -1911,18 +1871,23 @@ static ssize_t rbd_image_refresh(struct device *dev, size_t size) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - int ret; + int rc; + int ret = size; + + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - ret = rbd_refresh_header(rbd_dev, NULL); + rc = __rbd_refresh_header(rbd_dev); + if (rc < 0) + ret = rc; - return ret < 0 ? ret : size; + mutex_unlock(&ctl_mutex); + return ret; } static DEVICE_ATTR(size, S_IRUGO, rbd_size_show, NULL); static DEVICE_ATTR(major, S_IRUGO, rbd_major_show, NULL); static DEVICE_ATTR(client_id, S_IRUGO, rbd_client_id_show, NULL); static DEVICE_ATTR(pool, S_IRUGO, rbd_pool_show, NULL); -static DEVICE_ATTR(pool_id, S_IRUGO, rbd_pool_id_show, NULL); static DEVICE_ATTR(name, S_IRUGO, rbd_name_show, NULL); static DEVICE_ATTR(refresh, S_IWUSR, NULL, rbd_image_refresh); static DEVICE_ATTR(current_snap, S_IRUGO, rbd_snap_show, NULL); @@ -1933,7 +1898,6 @@ static struct attribute *rbd_attrs[] = { &dev_attr_major.attr, &dev_attr_client_id.attr, &dev_attr_pool.attr, - &dev_attr_pool_id.attr, &dev_attr_name.attr, &dev_attr_current_snap.attr, &dev_attr_refresh.attr, @@ -2013,13 +1977,15 @@ static struct device_type rbd_snap_device_type = { .release = rbd_snap_dev_release, }; -static void __rbd_remove_snap_dev(struct rbd_snap *snap) +static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev, + struct rbd_snap *snap) { list_del(&snap->node); device_unregister(&snap->dev); } -static int rbd_register_snap_dev(struct rbd_snap *snap, +static int rbd_register_snap_dev(struct rbd_device *rbd_dev, + struct rbd_snap *snap, struct device *parent) { struct device *dev = &snap->dev; @@ -2034,36 +2000,29 @@ static int rbd_register_snap_dev(struct rbd_snap *snap, return ret; } -static struct rbd_snap *__rbd_add_snap_dev(struct rbd_device *rbd_dev, - int i, const char *name) +static int __rbd_add_snap_dev(struct rbd_device *rbd_dev, + int i, const char *name, + struct rbd_snap **snapp) { - struct rbd_snap *snap; int ret; - - snap = kzalloc(sizeof (*snap), GFP_KERNEL); + struct rbd_snap *snap = kzalloc(sizeof(*snap), GFP_KERNEL); if (!snap) - return ERR_PTR(-ENOMEM); - - ret = -ENOMEM; + return -ENOMEM; snap->name = kstrdup(name, GFP_KERNEL); - if (!snap->name) - goto err; - snap->size = rbd_dev->header.snap_sizes[i]; snap->id = rbd_dev->header.snapc->snaps[i]; if (device_is_registered(&rbd_dev->dev)) { - ret = rbd_register_snap_dev(snap, &rbd_dev->dev); + ret = rbd_register_snap_dev(rbd_dev, snap, + &rbd_dev->dev); if (ret < 0) goto err; } - - return snap; - + *snapp = snap; + return 0; err: kfree(snap->name); kfree(snap); - - return ERR_PTR(ret); + return ret; } /* @@ -2096,6 +2055,7 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) const char *name, *first_name; int i = rbd_dev->header.total_snaps; struct rbd_snap *snap, *old_snap = NULL; + int ret; struct list_head *p, *n; first_name = rbd_dev->header.snap_names; @@ -2110,15 +2070,8 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) cur_id = rbd_dev->header.snapc->snaps[i - 1]; if (!i || old_snap->id < cur_id) { - /* - * old_snap->id was skipped, thus was - * removed. If this rbd_dev is mapped to - * the removed snapshot, record that it no - * longer exists, to prevent further I/O. - */ - if (rbd_dev->snap_id == old_snap->id) - rbd_dev->snap_exists = false; - __rbd_remove_snap_dev(old_snap); + /* old_snap->id was skipped, thus was removed */ + __rbd_remove_snap_dev(rbd_dev, old_snap); continue; } if (old_snap->id == cur_id) { @@ -2138,9 +2091,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) if (cur_id >= old_snap->id) break; /* a new snapshot */ - snap = __rbd_add_snap_dev(rbd_dev, i - 1, name); - if (IS_ERR(snap)) - return PTR_ERR(snap); + ret = __rbd_add_snap_dev(rbd_dev, i - 1, name, &snap); + if (ret < 0) + return ret; /* note that we add it backward so using n and not p */ list_add(&snap->node, n); @@ -2154,9 +2107,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev) WARN_ON(1); return -EINVAL; } - snap = __rbd_add_snap_dev(rbd_dev, i - 1, name); - if (IS_ERR(snap)) - return PTR_ERR(snap); + ret = __rbd_add_snap_dev(rbd_dev, i - 1, name, &snap); + if (ret < 0) + return ret; list_add(&snap->node, &rbd_dev->snaps); } @@ -2176,13 +2129,14 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev) dev->type = &rbd_device_type; dev->parent = &rbd_root_dev; dev->release = rbd_dev_release; - dev_set_name(dev, "%d", rbd_dev->dev_id); + dev_set_name(dev, "%d", rbd_dev->id); ret = device_register(dev); if (ret < 0) goto out; list_for_each_entry(snap, &rbd_dev->snaps, node) { - ret = rbd_register_snap_dev(snap, &rbd_dev->dev); + ret = rbd_register_snap_dev(rbd_dev, snap, + &rbd_dev->dev); if (ret < 0) break; } @@ -2201,9 +2155,12 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev) int ret, rc; do { - ret = rbd_req_sync_watch(rbd_dev); + ret = rbd_req_sync_watch(rbd_dev, rbd_dev->obj_md_name, + rbd_dev->header.obj_version); if (ret == -ERANGE) { - rc = rbd_refresh_header(rbd_dev, NULL); + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + rc = __rbd_refresh_header(rbd_dev); + mutex_unlock(&ctl_mutex); if (rc < 0) return rc; } @@ -2220,7 +2177,7 @@ static atomic64_t rbd_id_max = ATOMIC64_INIT(0); */ static void rbd_id_get(struct rbd_device *rbd_dev) { - rbd_dev->dev_id = atomic64_inc_return(&rbd_id_max); + rbd_dev->id = atomic64_inc_return(&rbd_id_max); spin_lock(&rbd_dev_list_lock); list_add_tail(&rbd_dev->node, &rbd_dev_list); @@ -2234,7 +2191,7 @@ static void rbd_id_get(struct rbd_device *rbd_dev) static void rbd_id_put(struct rbd_device *rbd_dev) { struct list_head *tmp; - int rbd_id = rbd_dev->dev_id; + int rbd_id = rbd_dev->id; int max_id; BUG_ON(rbd_id < 1); @@ -2325,58 +2282,19 @@ static inline size_t copy_token(const char **buf, } /* - * Finds the next token in *buf, dynamically allocates a buffer big - * enough to hold a copy of it, and copies the token into the new - * buffer. The copy is guaranteed to be terminated with '\0'. Note - * that a duplicate buffer is created even for a zero-length token. - * - * Returns a pointer to the newly-allocated duplicate, or a null - * pointer if memory for the duplicate was not available. If - * the lenp argument is a non-null pointer, the length of the token - * (not including the '\0') is returned in *lenp. - * - * If successful, the *buf pointer will be updated to point beyond - * the end of the found token. - * - * Note: uses GFP_KERNEL for allocation. - */ -static inline char *dup_token(const char **buf, size_t *lenp) -{ - char *dup; - size_t len; - - len = next_token(buf); - dup = kmalloc(len + 1, GFP_KERNEL); - if (!dup) - return NULL; - - memcpy(dup, *buf, len); - *(dup + len) = '\0'; - *buf += len; - - if (lenp) - *lenp = len; - - return dup; -} - -/* - * This fills in the pool_name, image_name, image_name_len, snap_name, + * This fills in the pool_name, obj, obj_len, snap_name, obj_len, * rbd_dev, rbd_md_name, and name fields of the given rbd_dev, based * on the list of monitor addresses and other options provided via * /sys/bus/rbd/add. - * - * Note: rbd_dev is assumed to have been initially zero-filled. */ static int rbd_add_parse_args(struct rbd_device *rbd_dev, const char *buf, const char **mon_addrs, size_t *mon_addrs_size, char *options, - size_t options_size) + size_t options_size) { - size_t len; - int ret; + size_t len; /* The first four tokens are required */ @@ -2392,74 +2310,56 @@ static int rbd_add_parse_args(struct rbd_device *rbd_dev, if (!len || len >= options_size) return -EINVAL; - ret = -ENOMEM; - rbd_dev->pool_name = dup_token(&buf, NULL); - if (!rbd_dev->pool_name) - goto out_err; + len = copy_token(&buf, rbd_dev->pool_name, sizeof (rbd_dev->pool_name)); + if (!len || len >= sizeof (rbd_dev->pool_name)) + return -EINVAL; - rbd_dev->image_name = dup_token(&buf, &rbd_dev->image_name_len); - if (!rbd_dev->image_name) - goto out_err; + len = copy_token(&buf, rbd_dev->obj, sizeof (rbd_dev->obj)); + if (!len || len >= sizeof (rbd_dev->obj)) + return -EINVAL; - /* Create the name of the header object */ + /* We have the object length in hand, save it. */ - rbd_dev->header_name = kmalloc(rbd_dev->image_name_len - + sizeof (RBD_SUFFIX), - GFP_KERNEL); - if (!rbd_dev->header_name) - goto out_err; - sprintf(rbd_dev->header_name, "%s%s", rbd_dev->image_name, RBD_SUFFIX); + rbd_dev->obj_len = len; + + BUILD_BUG_ON(RBD_MAX_MD_NAME_LEN + < RBD_MAX_OBJ_NAME_LEN + sizeof (RBD_SUFFIX)); + sprintf(rbd_dev->obj_md_name, "%s%s", rbd_dev->obj, RBD_SUFFIX); /* - * The snapshot name is optional. If none is is supplied, - * we use the default value. + * The snapshot name is optional, but it's an error if it's + * too long. If no snapshot is supplied, fill in the default. */ - rbd_dev->snap_name = dup_token(&buf, &len); - if (!rbd_dev->snap_name) - goto out_err; - if (!len) { - /* Replace the empty name with the default */ - kfree(rbd_dev->snap_name); - rbd_dev->snap_name - = kmalloc(sizeof (RBD_SNAP_HEAD_NAME), GFP_KERNEL); - if (!rbd_dev->snap_name) - goto out_err; - + len = copy_token(&buf, rbd_dev->snap_name, sizeof (rbd_dev->snap_name)); + if (!len) memcpy(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME, sizeof (RBD_SNAP_HEAD_NAME)); - } + else if (len >= sizeof (rbd_dev->snap_name)) + return -EINVAL; return 0; - -out_err: - kfree(rbd_dev->header_name); - kfree(rbd_dev->image_name); - kfree(rbd_dev->pool_name); - rbd_dev->pool_name = NULL; - - return ret; } static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count) { - char *options; - struct rbd_device *rbd_dev = NULL; + struct rbd_device *rbd_dev; const char *mon_addrs = NULL; size_t mon_addrs_size = 0; + char *options = NULL; struct ceph_osd_client *osdc; int rc = -ENOMEM; if (!try_module_get(THIS_MODULE)) return -ENODEV; - options = kmalloc(count, GFP_KERNEL); - if (!options) - goto err_nomem; rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL); if (!rbd_dev) goto err_nomem; + options = kmalloc(count, GFP_KERNEL); + if (!options) + goto err_nomem; /* static rbd_device initialization */ spin_lock_init(&rbd_dev->lock); @@ -2467,13 +2367,15 @@ static ssize_t rbd_add(struct bus_type *bus, INIT_LIST_HEAD(&rbd_dev->snaps); init_rwsem(&rbd_dev->header_rwsem); + init_rwsem(&rbd_dev->header_rwsem); + /* generate unique id: find highest unique id, add one */ rbd_id_get(rbd_dev); /* Fill in the device name, now that we have its id. */ BUILD_BUG_ON(DEV_NAME_LEN < sizeof (RBD_DRV_NAME) + MAX_INT_FORMAT_WIDTH); - sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->dev_id); + sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->id); /* parse add command */ rc = rbd_add_parse_args(rbd_dev, buf, &mon_addrs, &mon_addrs_size, @@ -2493,7 +2395,7 @@ static ssize_t rbd_add(struct bus_type *bus, rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name); if (rc < 0) goto err_out_client; - rbd_dev->pool_id = rc; + rbd_dev->poolid = rc; /* register our block device */ rc = register_blkdev(0, rbd_dev->name); @@ -2533,16 +2435,10 @@ static ssize_t rbd_add(struct bus_type *bus, err_out_client: rbd_put_client(rbd_dev); err_put_id: - if (rbd_dev->pool_name) { - kfree(rbd_dev->snap_name); - kfree(rbd_dev->header_name); - kfree(rbd_dev->image_name); - kfree(rbd_dev->pool_name); - } rbd_id_put(rbd_dev); err_nomem: - kfree(rbd_dev); kfree(options); + kfree(rbd_dev); dout("Error adding device %s\n", buf); module_put(THIS_MODULE); @@ -2550,7 +2446,7 @@ static ssize_t rbd_add(struct bus_type *bus, return (ssize_t) rc; } -static struct rbd_device *__rbd_get_dev(unsigned long dev_id) +static struct rbd_device *__rbd_get_dev(unsigned long id) { struct list_head *tmp; struct rbd_device *rbd_dev; @@ -2558,7 +2454,7 @@ static struct rbd_device *__rbd_get_dev(unsigned long dev_id) spin_lock(&rbd_dev_list_lock); list_for_each(tmp, &rbd_dev_list) { rbd_dev = list_entry(tmp, struct rbd_device, node); - if (rbd_dev->dev_id == dev_id) { + if (rbd_dev->id == id) { spin_unlock(&rbd_dev_list_lock); return rbd_dev; } @@ -2578,7 +2474,7 @@ static void rbd_dev_release(struct device *dev) rbd_dev->watch_request); } if (rbd_dev->watch_event) - rbd_req_sync_unwatch(rbd_dev); + rbd_req_sync_unwatch(rbd_dev, rbd_dev->obj_md_name); rbd_put_client(rbd_dev); @@ -2587,10 +2483,6 @@ static void rbd_dev_release(struct device *dev) unregister_blkdev(rbd_dev->major, rbd_dev->name); /* done with the id, and with the rbd_dev */ - kfree(rbd_dev->snap_name); - kfree(rbd_dev->header_name); - kfree(rbd_dev->pool_name); - kfree(rbd_dev->image_name); rbd_id_put(rbd_dev); kfree(rbd_dev); @@ -2652,7 +2544,7 @@ static ssize_t rbd_snap_add(struct device *dev, if (ret < 0) goto err_unlock; - ret = __rbd_refresh_header(rbd_dev, NULL); + ret = __rbd_refresh_header(rbd_dev); if (ret < 0) goto err_unlock; @@ -2661,7 +2553,7 @@ static ssize_t rbd_snap_add(struct device *dev, mutex_unlock(&ctl_mutex); /* make a best effort, don't error if failed */ - rbd_req_sync_notify(rbd_dev); + rbd_req_sync_notify(rbd_dev, rbd_dev->obj_md_name); ret = count; kfree(name); diff --git a/trunk/drivers/block/rbd_types.h b/trunk/drivers/block/rbd_types.h index 0924e9e41a60..950708688f17 100644 --- a/trunk/drivers/block/rbd_types.h +++ b/trunk/drivers/block/rbd_types.h @@ -31,6 +31,7 @@ #define RBD_MIN_OBJ_ORDER 16 #define RBD_MAX_OBJ_ORDER 30 +#define RBD_MAX_OBJ_NAME_LEN 96 #define RBD_MAX_SEG_NAME_LEN 128 #define RBD_COMP_NONE 0 diff --git a/trunk/drivers/block/umem.c b/trunk/drivers/block/umem.c index eb0d8216f557..9a72277a31df 100644 --- a/trunk/drivers/block/umem.c +++ b/trunk/drivers/block/umem.c @@ -513,19 +513,42 @@ static void process_page(unsigned long data) } } -static void mm_unplug(struct blk_plug_cb *cb, bool from_schedule) +struct mm_plug_cb { + struct blk_plug_cb cb; + struct cardinfo *card; +}; + +static void mm_unplug(struct blk_plug_cb *cb) { - struct cardinfo *card = cb->data; + struct mm_plug_cb *mmcb = container_of(cb, struct mm_plug_cb, cb); - spin_lock_irq(&card->lock); - activate(card); - spin_unlock_irq(&card->lock); - kfree(cb); + spin_lock_irq(&mmcb->card->lock); + activate(mmcb->card); + spin_unlock_irq(&mmcb->card->lock); + kfree(mmcb); } static int mm_check_plugged(struct cardinfo *card) { - return !!blk_check_plugged(mm_unplug, card, sizeof(struct blk_plug_cb)); + struct blk_plug *plug = current->plug; + struct mm_plug_cb *mmcb; + + if (!plug) + return 0; + + list_for_each_entry(mmcb, &plug->cb_list, cb.list) { + if (mmcb->cb.callback == mm_unplug && mmcb->card == card) + return 1; + } + /* Not currently on the callback list */ + mmcb = kmalloc(sizeof(*mmcb), GFP_ATOMIC); + if (!mmcb) + return 0; + + mmcb->card = card; + mmcb->cb.callback = mm_unplug; + list_add(&mmcb->cb.list, &plug->cb_list); + return 1; } static void mm_make_request(struct request_queue *q, struct bio *bio) diff --git a/trunk/drivers/block/xen-blkfront.c b/trunk/drivers/block/xen-blkfront.c index 2c2d2e5c1597..e4fb3374dcd2 100644 --- a/trunk/drivers/block/xen-blkfront.c +++ b/trunk/drivers/block/xen-blkfront.c @@ -888,8 +888,9 @@ static int setup_blkring(struct xenbus_device *dev, if (err) goto fail; - err = bind_evtchn_to_irqhandler(info->evtchn, blkif_interrupt, 0, - "blkif", info); + err = bind_evtchn_to_irqhandler(info->evtchn, + blkif_interrupt, + IRQF_SAMPLE_RANDOM, "blkif", info); if (err <= 0) { xenbus_dev_fatal(dev, err, "bind_evtchn_to_irqhandler failed"); diff --git a/trunk/drivers/char/random.c b/trunk/drivers/char/random.c index b86eae9b77df..4ec04a754733 100644 --- a/trunk/drivers/char/random.c +++ b/trunk/drivers/char/random.c @@ -125,26 +125,21 @@ * The current exported interfaces for gathering environmental noise * from the devices are: * - * void add_device_randomness(const void *buf, unsigned int size); * void add_input_randomness(unsigned int type, unsigned int code, * unsigned int value); - * void add_interrupt_randomness(int irq, int irq_flags); + * void add_interrupt_randomness(int irq); * void add_disk_randomness(struct gendisk *disk); * - * add_device_randomness() is for adding data to the random pool that - * is likely to differ between two devices (or possibly even per boot). - * This would be things like MAC addresses or serial numbers, or the - * read-out of the RTC. This does *not* add any actual entropy to the - * pool, but it initializes the pool to different values for devices - * that might otherwise be identical and have very little entropy - * available to them (particularly common in the embedded world). - * * add_input_randomness() uses the input layer interrupt timing, as well as * the event type information from the hardware. * - * add_interrupt_randomness() uses the interrupt timing as random - * inputs to the entropy pool. Using the cycle counters and the irq source - * as inputs, it feeds the randomness roughly once a second. + * add_interrupt_randomness() uses the inter-interrupt timing as random + * inputs to the entropy pool. Note that not all interrupts are good + * sources of randomness! For example, the timer interrupts is not a + * good choice, because the periodicity of the interrupts is too + * regular, and hence predictable to an attacker. Network Interface + * Controller interrupts are a better measure, since the timing of the + * NIC interrupts are more unpredictable. * * add_disk_randomness() uses what amounts to the seek time of block * layer request events, on a per-disk_devt basis, as input to the @@ -253,8 +248,6 @@ #include #include #include -#include -#include #ifdef CONFIG_GENERIC_HARDIRQS # include @@ -263,12 +256,8 @@ #include #include #include -#include #include -#define CREATE_TRACE_POINTS -#include - /* * Configuration information */ @@ -277,8 +266,6 @@ #define SEC_XFER_SIZE 512 #define EXTRACT_SIZE 10 -#define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long)) - /* * The minimum number of bits of entropy before we wake up a read on * /dev/random. Should be enough to do a significant reseed. @@ -433,10 +420,8 @@ struct entropy_store { /* read-write data: */ spinlock_t lock; unsigned add_ptr; - unsigned input_rotate; int entropy_count; - int entropy_total; - unsigned int initialized:1; + int input_rotate; __u8 last_data[EXTRACT_SIZE]; }; @@ -469,10 +454,6 @@ static struct entropy_store nonblocking_pool = { .pool = nonblocking_pool_data }; -static __u32 const twist_table[8] = { - 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, - 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; - /* * This function adds bytes into the entropy "pool". It does not * update the entropy estimate. The caller should call @@ -483,24 +464,29 @@ static __u32 const twist_table[8] = { * it's cheap to do so and helps slightly in the expected case where * the entropy is concentrated in the low-order bits. */ -static void _mix_pool_bytes(struct entropy_store *r, const void *in, - int nbytes, __u8 out[64]) +static void mix_pool_bytes_extract(struct entropy_store *r, const void *in, + int nbytes, __u8 out[64]) { + static __u32 const twist_table[8] = { + 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; unsigned long i, j, tap1, tap2, tap3, tap4, tap5; int input_rotate; int wordmask = r->poolinfo->poolwords - 1; const char *bytes = in; __u32 w; + unsigned long flags; + /* Taps are constant, so we can load them without holding r->lock. */ tap1 = r->poolinfo->tap1; tap2 = r->poolinfo->tap2; tap3 = r->poolinfo->tap3; tap4 = r->poolinfo->tap4; tap5 = r->poolinfo->tap5; - smp_rmb(); - input_rotate = ACCESS_ONCE(r->input_rotate); - i = ACCESS_ONCE(r->add_ptr); + spin_lock_irqsave(&r->lock, flags); + input_rotate = r->input_rotate; + i = r->add_ptr; /* mix one byte at a time to simplify size handling and churn faster */ while (nbytes--) { @@ -527,61 +513,19 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in, input_rotate += i ? 7 : 14; } - ACCESS_ONCE(r->input_rotate) = input_rotate; - ACCESS_ONCE(r->add_ptr) = i; - smp_wmb(); + r->input_rotate = input_rotate; + r->add_ptr = i; if (out) for (j = 0; j < 16; j++) ((__u32 *)out)[j] = r->pool[(i - j) & wordmask]; -} - -static void __mix_pool_bytes(struct entropy_store *r, const void *in, - int nbytes, __u8 out[64]) -{ - trace_mix_pool_bytes_nolock(r->name, nbytes, _RET_IP_); - _mix_pool_bytes(r, in, nbytes, out); -} - -static void mix_pool_bytes(struct entropy_store *r, const void *in, - int nbytes, __u8 out[64]) -{ - unsigned long flags; - trace_mix_pool_bytes(r->name, nbytes, _RET_IP_); - spin_lock_irqsave(&r->lock, flags); - _mix_pool_bytes(r, in, nbytes, out); spin_unlock_irqrestore(&r->lock, flags); } -struct fast_pool { - __u32 pool[4]; - unsigned long last; - unsigned short count; - unsigned char rotate; - unsigned char last_timer_intr; -}; - -/* - * This is a fast mixing routine used by the interrupt randomness - * collector. It's hardcoded for an 128 bit pool and assumes that any - * locks that might be needed are taken by the caller. - */ -static void fast_mix(struct fast_pool *f, const void *in, int nbytes) +static void mix_pool_bytes(struct entropy_store *r, const void *in, int bytes) { - const char *bytes = in; - __u32 w; - unsigned i = f->count; - unsigned input_rotate = f->rotate; - - while (nbytes--) { - w = rol32(*bytes++, input_rotate & 31) ^ f->pool[i & 3] ^ - f->pool[(i + 1) & 3]; - f->pool[i & 3] = (w >> 3) ^ twist_table[w & 7]; - input_rotate += (i++ & 3) ? 7 : 14; - } - f->count = i; - f->rotate = input_rotate; + mix_pool_bytes_extract(r, in, bytes, NULL); } /* @@ -589,38 +533,30 @@ static void fast_mix(struct fast_pool *f, const void *in, int nbytes) */ static void credit_entropy_bits(struct entropy_store *r, int nbits) { - int entropy_count, orig; + unsigned long flags; + int entropy_count; if (!nbits) return; + spin_lock_irqsave(&r->lock, flags); + DEBUG_ENT("added %d entropy credits to %s\n", nbits, r->name); -retry: - entropy_count = orig = ACCESS_ONCE(r->entropy_count); + entropy_count = r->entropy_count; entropy_count += nbits; - if (entropy_count < 0) { DEBUG_ENT("negative entropy/overflow\n"); entropy_count = 0; } else if (entropy_count > r->poolinfo->POOLBITS) entropy_count = r->poolinfo->POOLBITS; - if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) - goto retry; - - if (!r->initialized && nbits > 0) { - r->entropy_total += nbits; - if (r->entropy_total > 128) - r->initialized = 1; - } - - trace_credit_entropy_bits(r->name, nbits, entropy_count, - r->entropy_total, _RET_IP_); + r->entropy_count = entropy_count; /* should we wake readers? */ if (r == &input_pool && entropy_count >= random_read_wakeup_thresh) { wake_up_interruptible(&random_read_wait); kill_fasync(&fasync, SIGIO, POLL_IN); } + spin_unlock_irqrestore(&r->lock, flags); } /********************************************************************* @@ -636,24 +572,42 @@ struct timer_rand_state { unsigned dont_count_entropy:1; }; -/* - * Add device- or boot-specific data to the input and nonblocking - * pools to help initialize them to unique values. - * - * None of this adds any entropy, it is meant to avoid the - * problem of the nonblocking pool having similar initial state - * across largely identical devices. - */ -void add_device_randomness(const void *buf, unsigned int size) +#ifndef CONFIG_GENERIC_HARDIRQS + +static struct timer_rand_state *irq_timer_state[NR_IRQS]; + +static struct timer_rand_state *get_timer_rand_state(unsigned int irq) +{ + return irq_timer_state[irq]; +} + +static void set_timer_rand_state(unsigned int irq, + struct timer_rand_state *state) { - unsigned long time = get_cycles() ^ jiffies; + irq_timer_state[irq] = state; +} + +#else - mix_pool_bytes(&input_pool, buf, size, NULL); - mix_pool_bytes(&input_pool, &time, sizeof(time), NULL); - mix_pool_bytes(&nonblocking_pool, buf, size, NULL); - mix_pool_bytes(&nonblocking_pool, &time, sizeof(time), NULL); +static struct timer_rand_state *get_timer_rand_state(unsigned int irq) +{ + struct irq_desc *desc; + + desc = irq_to_desc(irq); + + return desc->timer_rand_state; } -EXPORT_SYMBOL(add_device_randomness); + +static void set_timer_rand_state(unsigned int irq, + struct timer_rand_state *state) +{ + struct irq_desc *desc; + + desc = irq_to_desc(irq); + + desc->timer_rand_state = state; +} +#endif static struct timer_rand_state input_timer_state; @@ -683,9 +637,13 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num) goto out; sample.jiffies = jiffies; - sample.cycles = get_cycles(); + + /* Use arch random value, fall back to cycles */ + if (!arch_get_random_int(&sample.cycles)) + sample.cycles = get_cycles(); + sample.num = num; - mix_pool_bytes(&input_pool, &sample, sizeof(sample), NULL); + mix_pool_bytes(&input_pool, &sample, sizeof(sample)); /* * Calculate number of bits of randomness we probably added. @@ -742,48 +700,17 @@ void add_input_randomness(unsigned int type, unsigned int code, } EXPORT_SYMBOL_GPL(add_input_randomness); -static DEFINE_PER_CPU(struct fast_pool, irq_randomness); - -void add_interrupt_randomness(int irq, int irq_flags) +void add_interrupt_randomness(int irq) { - struct entropy_store *r; - struct fast_pool *fast_pool = &__get_cpu_var(irq_randomness); - struct pt_regs *regs = get_irq_regs(); - unsigned long now = jiffies; - __u32 input[4], cycles = get_cycles(); - - input[0] = cycles ^ jiffies; - input[1] = irq; - if (regs) { - __u64 ip = instruction_pointer(regs); - input[2] = ip; - input[3] = ip >> 32; - } + struct timer_rand_state *state; - fast_mix(fast_pool, input, sizeof(input)); + state = get_timer_rand_state(irq); - if ((fast_pool->count & 1023) && - !time_after(now, fast_pool->last + HZ)) + if (state == NULL) return; - fast_pool->last = now; - - r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool; - __mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool), NULL); - /* - * If we don't have a valid cycle counter, and we see - * back-to-back timer interrupts, then skip giving credit for - * any entropy. - */ - if (cycles == 0) { - if (irq_flags & __IRQF_TIMER) { - if (fast_pool->last_timer_intr) - return; - fast_pool->last_timer_intr = 1; - } else - fast_pool->last_timer_intr = 0; - } - credit_entropy_bits(r, 1); + DEBUG_ENT("irq event %d\n", irq); + add_timer_randomness(state, 0x100 + irq); } #ifdef CONFIG_BLOCK @@ -815,7 +742,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, */ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) { - __u32 tmp[OUTPUT_POOL_WORDS]; + __u32 tmp[OUTPUT_POOL_WORDS]; if (r->pull && r->entropy_count < nbytes * 8 && r->entropy_count < r->poolinfo->POOLBITS) { @@ -834,7 +761,7 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) bytes = extract_entropy(r->pull, tmp, bytes, random_read_wakeup_thresh / 8, rsvd); - mix_pool_bytes(r, tmp, bytes, NULL); + mix_pool_bytes(r, tmp, bytes); credit_entropy_bits(r, bytes*8); } } @@ -893,19 +820,13 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min, static void extract_buf(struct entropy_store *r, __u8 *out) { int i; - union { - __u32 w[5]; - unsigned long l[LONGS(EXTRACT_SIZE)]; - } hash; - __u32 workspace[SHA_WORKSPACE_WORDS]; + __u32 hash[5], workspace[SHA_WORKSPACE_WORDS]; __u8 extract[64]; - unsigned long flags; /* Generate a hash across the pool, 16 words (512 bits) at a time */ - sha_init(hash.w); - spin_lock_irqsave(&r->lock, flags); + sha_init(hash); for (i = 0; i < r->poolinfo->poolwords; i += 16) - sha_transform(hash.w, (__u8 *)(r->pool + i), workspace); + sha_transform(hash, (__u8 *)(r->pool + i), workspace); /* * We mix the hash back into the pool to prevent backtracking @@ -916,14 +837,13 @@ static void extract_buf(struct entropy_store *r, __u8 *out) * brute-forcing the feedback as hard as brute-forcing the * hash. */ - __mix_pool_bytes(r, hash.w, sizeof(hash.w), extract); - spin_unlock_irqrestore(&r->lock, flags); + mix_pool_bytes_extract(r, hash, sizeof(hash), extract); /* * To avoid duplicates, we atomically extract a portion of the * pool while mixing, and hash one final time. */ - sha_transform(hash.w, extract, workspace); + sha_transform(hash, extract, workspace); memset(extract, 0, sizeof(extract)); memset(workspace, 0, sizeof(workspace)); @@ -932,32 +852,20 @@ static void extract_buf(struct entropy_store *r, __u8 *out) * pattern, we fold it in half. Thus, we always feed back * twice as much data as we output. */ - hash.w[0] ^= hash.w[3]; - hash.w[1] ^= hash.w[4]; - hash.w[2] ^= rol32(hash.w[2], 16); - - /* - * If we have a architectural hardware random number - * generator, mix that in, too. - */ - for (i = 0; i < LONGS(EXTRACT_SIZE); i++) { - unsigned long v; - if (!arch_get_random_long(&v)) - break; - hash.l[i] ^= v; - } - - memcpy(out, &hash, EXTRACT_SIZE); - memset(&hash, 0, sizeof(hash)); + hash[0] ^= hash[3]; + hash[1] ^= hash[4]; + hash[2] ^= rol32(hash[2], 16); + memcpy(out, hash, EXTRACT_SIZE); + memset(hash, 0, sizeof(hash)); } static ssize_t extract_entropy(struct entropy_store *r, void *buf, - size_t nbytes, int min, int reserved) + size_t nbytes, int min, int reserved) { ssize_t ret = 0, i; __u8 tmp[EXTRACT_SIZE]; + unsigned long flags; - trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_); xfer_secondary_pool(r, nbytes); nbytes = account(r, nbytes, min, reserved); @@ -965,8 +873,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, extract_buf(r, tmp); if (fips_enabled) { - unsigned long flags; - spin_lock_irqsave(&r->lock, flags); if (!memcmp(tmp, r->last_data, EXTRACT_SIZE)) panic("Hardware RNG duplicated output!\n"); @@ -992,7 +898,6 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, ssize_t ret = 0, i; __u8 tmp[EXTRACT_SIZE]; - trace_extract_entropy_user(r->name, nbytes, r->entropy_count, _RET_IP_); xfer_secondary_pool(r, nbytes); nbytes = account(r, nbytes, 0, 0); @@ -1026,35 +931,17 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, /* * This function is the exported kernel interface. It returns some - * number of good random numbers, suitable for key generation, seeding - * TCP sequence numbers, etc. It does not use the hw random number - * generator, if available; use get_random_bytes_arch() for that. + * number of good random numbers, suitable for seeding TCP sequence + * numbers, etc. */ void get_random_bytes(void *buf, int nbytes) -{ - extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0); -} -EXPORT_SYMBOL(get_random_bytes); - -/* - * This function will use the architecture-specific hardware random - * number generator if it is available. The arch-specific hw RNG will - * almost certainly be faster than what we can do in software, but it - * is impossible to verify that it is implemented securely (as - * opposed, to, say, the AES encryption of a sequence number using a - * key known by the NSA). So it's useful if we need the speed, but - * only if we're willing to trust the hardware manufacturer not to - * have put in a back door. - */ -void get_random_bytes_arch(void *buf, int nbytes) { char *p = buf; - trace_get_random_bytes(nbytes, _RET_IP_); while (nbytes) { unsigned long v; int chunk = min(nbytes, (int)sizeof(unsigned long)); - + if (!arch_get_random_long(&v)) break; @@ -1063,11 +950,9 @@ void get_random_bytes_arch(void *buf, int nbytes) nbytes -= chunk; } - if (nbytes) - extract_entropy(&nonblocking_pool, p, nbytes, 0, 0); + extract_entropy(&nonblocking_pool, p, nbytes, 0, 0); } -EXPORT_SYMBOL(get_random_bytes_arch); - +EXPORT_SYMBOL(get_random_bytes); /* * init_std_data - initialize pool with system data @@ -1081,30 +966,23 @@ EXPORT_SYMBOL(get_random_bytes_arch); static void init_std_data(struct entropy_store *r) { int i; - ktime_t now = ktime_get_real(); - unsigned long rv; + ktime_t now; + unsigned long flags; + spin_lock_irqsave(&r->lock, flags); r->entropy_count = 0; - r->entropy_total = 0; - mix_pool_bytes(r, &now, sizeof(now), NULL); - for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof(rv)) { - if (!arch_get_random_long(&rv)) + spin_unlock_irqrestore(&r->lock, flags); + + now = ktime_get_real(); + mix_pool_bytes(r, &now, sizeof(now)); + for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof flags) { + if (!arch_get_random_long(&flags)) break; - mix_pool_bytes(r, &rv, sizeof(rv), NULL); + mix_pool_bytes(r, &flags, sizeof(flags)); } - mix_pool_bytes(r, utsname(), sizeof(*(utsname())), NULL); + mix_pool_bytes(r, utsname(), sizeof(*(utsname()))); } -/* - * Note that setup_arch() may call add_device_randomness() - * long before we get here. This allows seeding of the pools - * with some platform dependent data very early in the boot - * process. But it limits our options here. We must use - * statically allocated structures that already have all - * initializations complete at compile time. We should also - * take care not to overwrite the precious per platform data - * we were given. - */ static int rand_initialize(void) { init_std_data(&input_pool); @@ -1114,6 +992,24 @@ static int rand_initialize(void) } module_init(rand_initialize); +void rand_initialize_irq(int irq) +{ + struct timer_rand_state *state; + + state = get_timer_rand_state(irq); + + if (state) + return; + + /* + * If kzalloc returns null, we just won't use that entropy + * source. + */ + state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL); + if (state) + set_timer_rand_state(irq, state); +} + #ifdef CONFIG_BLOCK void rand_initialize_disk(struct gendisk *disk) { @@ -1221,7 +1117,7 @@ write_pool(struct entropy_store *r, const char __user *buffer, size_t count) count -= bytes; p += bytes; - mix_pool_bytes(r, buf, bytes, NULL); + mix_pool_bytes(r, buf, bytes); cond_resched(); } @@ -1383,7 +1279,6 @@ static int proc_do_uuid(ctl_table *table, int write, } static int sysctl_poolsize = INPUT_POOL_WORDS * 32; -extern ctl_table random_table[]; ctl_table random_table[] = { { .procname = "poolsize", @@ -1449,7 +1344,7 @@ late_initcall(random_int_secret_init); * value is not cryptographically secure but for several uses the cost of * depleting entropy is too high */ -static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash); +DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash); unsigned int get_random_int(void) { __u32 *hash; diff --git a/trunk/drivers/crypto/n2_core.c b/trunk/drivers/crypto/n2_core.c index a8bd0310f8fe..67b97c5fd859 100644 --- a/trunk/drivers/crypto/n2_core.c +++ b/trunk/drivers/crypto/n2_core.c @@ -1610,7 +1610,8 @@ static int spu_map_ino(struct platform_device *dev, struct spu_mdesc_info *ip, sprintf(p->irq_name, "%s-%d", irq_name, index); - return request_irq(p->irq, handler, 0, p->irq_name, p); + return request_irq(p->irq, handler, IRQF_SAMPLE_RANDOM, + p->irq_name, p); } static struct kmem_cache *queue_cache[2]; diff --git a/trunk/drivers/dma/Kconfig b/trunk/drivers/dma/Kconfig index d06ea2950dd9..d45cf1bcbde5 100644 --- a/trunk/drivers/dma/Kconfig +++ b/trunk/drivers/dma/Kconfig @@ -53,7 +53,6 @@ config AMBA_PL08X bool "ARM PrimeCell PL080 or PL081 support" depends on ARM_AMBA && EXPERIMENTAL select DMA_ENGINE - select DMA_VIRTUAL_CHANNELS help Platform has a PL08x DMAC device which can provide DMA engine support @@ -270,7 +269,6 @@ config DMA_SA11X0 tristate "SA-11x0 DMA support" depends on ARCH_SA1100 select DMA_ENGINE - select DMA_VIRTUAL_CHANNELS help Support the DMA engine found on Intel StrongARM SA-1100 and SA-1110 SoCs. This DMA engine can only be used with on-chip @@ -286,18 +284,9 @@ config MMP_TDMA Say Y here if you enabled MMP ADMA, otherwise say N. -config DMA_OMAP - tristate "OMAP DMA support" - depends on ARCH_OMAP - select DMA_ENGINE - select DMA_VIRTUAL_CHANNELS - config DMA_ENGINE bool -config DMA_VIRTUAL_CHANNELS - tristate - comment "DMA Clients" depends on DMA_ENGINE diff --git a/trunk/drivers/dma/Makefile b/trunk/drivers/dma/Makefile index 4cf6b128ab9a..640356add0a3 100644 --- a/trunk/drivers/dma/Makefile +++ b/trunk/drivers/dma/Makefile @@ -2,7 +2,6 @@ ccflags-$(CONFIG_DMADEVICES_DEBUG) := -DDEBUG ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG obj-$(CONFIG_DMA_ENGINE) += dmaengine.o -obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o obj-$(CONFIG_NET_DMA) += iovlock.o obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o obj-$(CONFIG_DMATEST) += dmatest.o @@ -31,4 +30,3 @@ obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o -obj-$(CONFIG_DMA_OMAP) += omap-dma.o diff --git a/trunk/drivers/dma/amba-pl08x.c b/trunk/drivers/dma/amba-pl08x.c index 6fbeebb9486f..49ecbbb8932d 100644 --- a/trunk/drivers/dma/amba-pl08x.c +++ b/trunk/drivers/dma/amba-pl08x.c @@ -86,12 +86,10 @@ #include #include "dmaengine.h" -#include "virt-dma.h" #define DRIVER_NAME "pl08xdmac" static struct amba_driver pl08x_amba_driver; -struct pl08x_driver_data; /** * struct vendor_data - vendor-specific config parameters for PL08x derivatives @@ -120,123 +118,6 @@ struct pl08x_lli { u32 cctl; }; -/** - * struct pl08x_bus_data - information of source or destination - * busses for a transfer - * @addr: current address - * @maxwidth: the maximum width of a transfer on this bus - * @buswidth: the width of this bus in bytes: 1, 2 or 4 - */ -struct pl08x_bus_data { - dma_addr_t addr; - u8 maxwidth; - u8 buswidth; -}; - -/** - * struct pl08x_phy_chan - holder for the physical channels - * @id: physical index to this channel - * @lock: a lock to use when altering an instance of this struct - * @serving: the virtual channel currently being served by this physical - * channel - * @locked: channel unavailable for the system, e.g. dedicated to secure - * world - */ -struct pl08x_phy_chan { - unsigned int id; - void __iomem *base; - spinlock_t lock; - struct pl08x_dma_chan *serving; - bool locked; -}; - -/** - * struct pl08x_sg - structure containing data per sg - * @src_addr: src address of sg - * @dst_addr: dst address of sg - * @len: transfer len in bytes - * @node: node for txd's dsg_list - */ -struct pl08x_sg { - dma_addr_t src_addr; - dma_addr_t dst_addr; - size_t len; - struct list_head node; -}; - -/** - * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor - * @vd: virtual DMA descriptor - * @dsg_list: list of children sg's - * @llis_bus: DMA memory address (physical) start for the LLIs - * @llis_va: virtual memory address start for the LLIs - * @cctl: control reg values for current txd - * @ccfg: config reg values for current txd - * @done: this marks completed descriptors, which should not have their - * mux released. - */ -struct pl08x_txd { - struct virt_dma_desc vd; - struct list_head dsg_list; - dma_addr_t llis_bus; - struct pl08x_lli *llis_va; - /* Default cctl value for LLIs */ - u32 cctl; - /* - * Settings to be put into the physical channel when we - * trigger this txd. Other registers are in llis_va[0]. - */ - u32 ccfg; - bool done; -}; - -/** - * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel - * states - * @PL08X_CHAN_IDLE: the channel is idle - * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport - * channel and is running a transfer on it - * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport - * channel, but the transfer is currently paused - * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport - * channel to become available (only pertains to memcpy channels) - */ -enum pl08x_dma_chan_state { - PL08X_CHAN_IDLE, - PL08X_CHAN_RUNNING, - PL08X_CHAN_PAUSED, - PL08X_CHAN_WAITING, -}; - -/** - * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel - * @vc: wrappped virtual channel - * @phychan: the physical channel utilized by this channel, if there is one - * @name: name of channel - * @cd: channel platform data - * @runtime_addr: address for RX/TX according to the runtime config - * @at: active transaction on this channel - * @lock: a lock for this channel data - * @host: a pointer to the host (internal use) - * @state: whether the channel is idle, paused, running etc - * @slave: whether this channel is a device (slave) or for memcpy - * @signal: the physical DMA request signal which this channel is using - * @mux_use: count of descriptors using this DMA request signal setting - */ -struct pl08x_dma_chan { - struct virt_dma_chan vc; - struct pl08x_phy_chan *phychan; - const char *name; - const struct pl08x_channel_data *cd; - struct dma_slave_config cfg; - struct pl08x_txd *at; - struct pl08x_driver_data *host; - enum pl08x_dma_chan_state state; - bool slave; - int signal; - unsigned mux_use; -}; - /** * struct pl08x_driver_data - the local state holder for the PL08x * @slave: slave engine for this instance @@ -247,6 +128,7 @@ struct pl08x_dma_chan { * @pd: platform data passed in from the platform/machine * @phy_chans: array of data for the physical channels * @pool: a pool for the LLI descriptors + * @pool_ctr: counter of LLIs in the pool * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI * fetches * @mem_buses: set to indicate memory transfers on AHB2. @@ -261,8 +143,10 @@ struct pl08x_driver_data { struct pl08x_platform_data *pd; struct pl08x_phy_chan *phy_chans; struct dma_pool *pool; + int pool_ctr; u8 lli_buses; u8 mem_buses; + spinlock_t lock; }; /* @@ -278,51 +162,12 @@ struct pl08x_driver_data { static inline struct pl08x_dma_chan *to_pl08x_chan(struct dma_chan *chan) { - return container_of(chan, struct pl08x_dma_chan, vc.chan); + return container_of(chan, struct pl08x_dma_chan, chan); } static inline struct pl08x_txd *to_pl08x_txd(struct dma_async_tx_descriptor *tx) { - return container_of(tx, struct pl08x_txd, vd.tx); -} - -/* - * Mux handling. - * - * This gives us the DMA request input to the PL08x primecell which the - * peripheral described by the channel data will be routed to, possibly - * via a board/SoC specific external MUX. One important point to note - * here is that this does not depend on the physical channel. - */ -static int pl08x_request_mux(struct pl08x_dma_chan *plchan) -{ - const struct pl08x_platform_data *pd = plchan->host->pd; - int ret; - - if (plchan->mux_use++ == 0 && pd->get_signal) { - ret = pd->get_signal(plchan->cd); - if (ret < 0) { - plchan->mux_use = 0; - return ret; - } - - plchan->signal = ret; - } - return 0; -} - -static void pl08x_release_mux(struct pl08x_dma_chan *plchan) -{ - const struct pl08x_platform_data *pd = plchan->host->pd; - - if (plchan->signal >= 0) { - WARN_ON(plchan->mux_use == 0); - - if (--plchan->mux_use == 0 && pd->put_signal) { - pd->put_signal(plchan->cd, plchan->signal); - plchan->signal = -1; - } - } + return container_of(tx, struct pl08x_txd, tx); } /* @@ -344,25 +189,20 @@ static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch) * been set when the LLIs were constructed. Poke them into the hardware * and start the transfer. */ -static void pl08x_start_next_txd(struct pl08x_dma_chan *plchan) +static void pl08x_start_txd(struct pl08x_dma_chan *plchan, + struct pl08x_txd *txd) { struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_phy_chan *phychan = plchan->phychan; - struct virt_dma_desc *vd = vchan_next_desc(&plchan->vc); - struct pl08x_txd *txd = to_pl08x_txd(&vd->tx); - struct pl08x_lli *lli; + struct pl08x_lli *lli = &txd->llis_va[0]; u32 val; - list_del(&txd->vd.node); - plchan->at = txd; /* Wait for channel inactive */ while (pl08x_phy_channel_busy(phychan)) cpu_relax(); - lli = &txd->llis_va[0]; - dev_vdbg(&pl08x->adev->dev, "WRITE channel %d: csrc=0x%08x, cdst=0x%08x, " "clli=0x%08x, cctl=0x%08x, ccfg=0x%08x\n", @@ -471,8 +311,10 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan) { struct pl08x_phy_chan *ch; struct pl08x_txd *txd; + unsigned long flags; size_t bytes = 0; + spin_lock_irqsave(&plchan->lock, flags); ch = plchan->phychan; txd = plchan->at; @@ -512,6 +354,18 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan) } } + /* Sum up all queued transactions */ + if (!list_empty(&plchan->pend_list)) { + struct pl08x_txd *txdi; + list_for_each_entry(txdi, &plchan->pend_list, node) { + struct pl08x_sg *dsg; + list_for_each_entry(dsg, &txd->dsg_list, node) + bytes += dsg->len; + } + } + + spin_unlock_irqrestore(&plchan->lock, flags); + return bytes; } @@ -537,6 +391,7 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x, if (!ch->locked && !ch->serving) { ch->serving = virt_chan; + ch->signal = -1; spin_unlock_irqrestore(&ch->lock, flags); break; } @@ -549,114 +404,25 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x, return NULL; } + pm_runtime_get_sync(&pl08x->adev->dev); return ch; } -/* Mark the physical channel as free. Note, this write is atomic. */ static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x, struct pl08x_phy_chan *ch) { - ch->serving = NULL; -} - -/* - * Try to allocate a physical channel. When successful, assign it to - * this virtual channel, and initiate the next descriptor. The - * virtual channel lock must be held at this point. - */ -static void pl08x_phy_alloc_and_start(struct pl08x_dma_chan *plchan) -{ - struct pl08x_driver_data *pl08x = plchan->host; - struct pl08x_phy_chan *ch; - - ch = pl08x_get_phy_channel(pl08x, plchan); - if (!ch) { - dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name); - plchan->state = PL08X_CHAN_WAITING; - return; - } - - dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n", - ch->id, plchan->name); - - plchan->phychan = ch; - plchan->state = PL08X_CHAN_RUNNING; - pl08x_start_next_txd(plchan); -} - -static void pl08x_phy_reassign_start(struct pl08x_phy_chan *ch, - struct pl08x_dma_chan *plchan) -{ - struct pl08x_driver_data *pl08x = plchan->host; - - dev_dbg(&pl08x->adev->dev, "reassigned physical channel %d for xfer on %s\n", - ch->id, plchan->name); - - /* - * We do this without taking the lock; we're really only concerned - * about whether this pointer is NULL or not, and we're guaranteed - * that this will only be called when it _already_ is non-NULL. - */ - ch->serving = plchan; - plchan->phychan = ch; - plchan->state = PL08X_CHAN_RUNNING; - pl08x_start_next_txd(plchan); -} - -/* - * Free a physical DMA channel, potentially reallocating it to another - * virtual channel if we have any pending. - */ -static void pl08x_phy_free(struct pl08x_dma_chan *plchan) -{ - struct pl08x_driver_data *pl08x = plchan->host; - struct pl08x_dma_chan *p, *next; - - retry: - next = NULL; - - /* Find a waiting virtual channel for the next transfer. */ - list_for_each_entry(p, &pl08x->memcpy.channels, vc.chan.device_node) - if (p->state == PL08X_CHAN_WAITING) { - next = p; - break; - } + unsigned long flags; - if (!next) { - list_for_each_entry(p, &pl08x->slave.channels, vc.chan.device_node) - if (p->state == PL08X_CHAN_WAITING) { - next = p; - break; - } - } + spin_lock_irqsave(&ch->lock, flags); - /* Ensure that the physical channel is stopped */ - pl08x_terminate_phy_chan(pl08x, plchan->phychan); + /* Stop the channel and clear its interrupts */ + pl08x_terminate_phy_chan(pl08x, ch); - if (next) { - bool success; + pm_runtime_put(&pl08x->adev->dev); - /* - * Eww. We know this isn't going to deadlock - * but lockdep probably doesn't. - */ - spin_lock(&next->vc.lock); - /* Re-check the state now that we have the lock */ - success = next->state == PL08X_CHAN_WAITING; - if (success) - pl08x_phy_reassign_start(plchan->phychan, next); - spin_unlock(&next->vc.lock); - - /* If the state changed, try to find another channel */ - if (!success) - goto retry; - } else { - /* No more jobs, so free up the physical channel */ - pl08x_put_phy_channel(pl08x, plchan->phychan); - } - - plchan->phychan = NULL; - plchan->state = PL08X_CHAN_IDLE; + /* Mark it as free */ + ch->serving = NULL; + spin_unlock_irqrestore(&ch->lock, flags); } /* @@ -819,6 +585,8 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, return 0; } + pl08x->pool_ctr++; + bd.txd = txd; bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0; cctl = txd->cctl; @@ -1034,14 +802,18 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, return num_llis; } +/* You should call this with the struct pl08x lock held */ static void pl08x_free_txd(struct pl08x_driver_data *pl08x, struct pl08x_txd *txd) { struct pl08x_sg *dsg, *_dsg; + /* Free the LLI */ if (txd->llis_va) dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus); + pl08x->pool_ctr--; + list_for_each_entry_safe(dsg, _dsg, &txd->dsg_list, node) { list_del(&dsg->node); kfree(dsg); @@ -1050,75 +822,133 @@ static void pl08x_free_txd(struct pl08x_driver_data *pl08x, kfree(txd); } -static void pl08x_unmap_buffers(struct pl08x_txd *txd) +static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, + struct pl08x_dma_chan *plchan) { - struct device *dev = txd->vd.tx.chan->device->dev; - struct pl08x_sg *dsg; - - if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) { - if (txd->vd.tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE) - list_for_each_entry(dsg, &txd->dsg_list, node) - dma_unmap_single(dev, dsg->src_addr, dsg->len, - DMA_TO_DEVICE); - else { - list_for_each_entry(dsg, &txd->dsg_list, node) - dma_unmap_page(dev, dsg->src_addr, dsg->len, - DMA_TO_DEVICE); + struct pl08x_txd *txdi = NULL; + struct pl08x_txd *next; + + if (!list_empty(&plchan->pend_list)) { + list_for_each_entry_safe(txdi, + next, &plchan->pend_list, node) { + list_del(&txdi->node); + pl08x_free_txd(pl08x, txdi); } } - if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) { - if (txd->vd.tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE) - list_for_each_entry(dsg, &txd->dsg_list, node) - dma_unmap_single(dev, dsg->dst_addr, dsg->len, - DMA_FROM_DEVICE); - else - list_for_each_entry(dsg, &txd->dsg_list, node) - dma_unmap_page(dev, dsg->dst_addr, dsg->len, - DMA_FROM_DEVICE); - } } -static void pl08x_desc_free(struct virt_dma_desc *vd) +/* + * The DMA ENGINE API + */ +static int pl08x_alloc_chan_resources(struct dma_chan *chan) { - struct pl08x_txd *txd = to_pl08x_txd(&vd->tx); - struct pl08x_dma_chan *plchan = to_pl08x_chan(vd->tx.chan); - - if (!plchan->slave) - pl08x_unmap_buffers(txd); - - if (!txd->done) - pl08x_release_mux(plchan); + return 0; +} - pl08x_free_txd(plchan->host, txd); +static void pl08x_free_chan_resources(struct dma_chan *chan) +{ } -static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, - struct pl08x_dma_chan *plchan) +/* + * This should be called with the channel plchan->lock held + */ +static int prep_phy_channel(struct pl08x_dma_chan *plchan, + struct pl08x_txd *txd) { - LIST_HEAD(head); - struct pl08x_txd *txd; + struct pl08x_driver_data *pl08x = plchan->host; + struct pl08x_phy_chan *ch; + int ret; - vchan_get_all_descriptors(&plchan->vc, &head); + /* Check if we already have a channel */ + if (plchan->phychan) { + ch = plchan->phychan; + goto got_channel; + } - while (!list_empty(&head)) { - txd = list_first_entry(&head, struct pl08x_txd, vd.node); - list_del(&txd->vd.node); - pl08x_desc_free(&txd->vd); + ch = pl08x_get_phy_channel(pl08x, plchan); + if (!ch) { + /* No physical channel available, cope with it */ + dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name); + return -EBUSY; } + + /* + * OK we have a physical channel: for memcpy() this is all we + * need, but for slaves the physical signals may be muxed! + * Can the platform allow us to use this channel? + */ + if (plchan->slave && pl08x->pd->get_signal) { + ret = pl08x->pd->get_signal(plchan); + if (ret < 0) { + dev_dbg(&pl08x->adev->dev, + "unable to use physical channel %d for transfer on %s due to platform restrictions\n", + ch->id, plchan->name); + /* Release physical channel & return */ + pl08x_put_phy_channel(pl08x, ch); + return -EBUSY; + } + ch->signal = ret; + } + + plchan->phychan = ch; + dev_dbg(&pl08x->adev->dev, "allocated physical channel %d and signal %d for xfer on %s\n", + ch->id, + ch->signal, + plchan->name); + +got_channel: + /* Assign the flow control signal to this channel */ + if (txd->direction == DMA_MEM_TO_DEV) + txd->ccfg |= ch->signal << PL080_CONFIG_DST_SEL_SHIFT; + else if (txd->direction == DMA_DEV_TO_MEM) + txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT; + + plchan->phychan_hold++; + + return 0; } -/* - * The DMA ENGINE API - */ -static int pl08x_alloc_chan_resources(struct dma_chan *chan) +static void release_phy_channel(struct pl08x_dma_chan *plchan) { - return 0; + struct pl08x_driver_data *pl08x = plchan->host; + + if ((plchan->phychan->signal >= 0) && pl08x->pd->put_signal) { + pl08x->pd->put_signal(plchan); + plchan->phychan->signal = -1; + } + pl08x_put_phy_channel(pl08x, plchan->phychan); + plchan->phychan = NULL; } -static void pl08x_free_chan_resources(struct dma_chan *chan) +static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx) { - /* Ensure all queued descriptors are freed */ - vchan_free_chan_resources(to_virt_chan(chan)); + struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan); + struct pl08x_txd *txd = to_pl08x_txd(tx); + unsigned long flags; + dma_cookie_t cookie; + + spin_lock_irqsave(&plchan->lock, flags); + cookie = dma_cookie_assign(tx); + + /* Put this onto the pending list */ + list_add_tail(&txd->node, &plchan->pend_list); + + /* + * If there was no physical channel available for this memcpy, + * stack the request up and indicate that the channel is waiting + * for a free physical channel. + */ + if (!plchan->slave && !plchan->phychan) { + /* Do this memcpy whenever there is a channel ready */ + plchan->state = PL08X_CHAN_WAITING; + plchan->waiting = txd; + } else { + plchan->phychan_hold--; + } + + spin_unlock_irqrestore(&plchan->lock, flags); + + return cookie; } static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt( @@ -1138,53 +968,23 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); - struct virt_dma_desc *vd; - unsigned long flags; enum dma_status ret; - size_t bytes = 0; ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_SUCCESS) return ret; - /* - * There's no point calculating the residue if there's - * no txstate to store the value. - */ - if (!txstate) { - if (plchan->state == PL08X_CHAN_PAUSED) - ret = DMA_PAUSED; - return ret; - } - - spin_lock_irqsave(&plchan->vc.lock, flags); - ret = dma_cookie_status(chan, cookie, txstate); - if (ret != DMA_SUCCESS) { - vd = vchan_find_desc(&plchan->vc, cookie); - if (vd) { - /* On the issued list, so hasn't been processed yet */ - struct pl08x_txd *txd = to_pl08x_txd(&vd->tx); - struct pl08x_sg *dsg; - - list_for_each_entry(dsg, &txd->dsg_list, node) - bytes += dsg->len; - } else { - bytes = pl08x_getbytes_chan(plchan); - } - } - spin_unlock_irqrestore(&plchan->vc.lock, flags); - /* * This cookie not complete yet * Get number of bytes left in the active transactions and queue */ - dma_set_residue(txstate, bytes); + dma_set_residue(txstate, pl08x_getbytes_chan(plchan)); - if (plchan->state == PL08X_CHAN_PAUSED && ret == DMA_IN_PROGRESS) - ret = DMA_PAUSED; + if (plchan->state == PL08X_CHAN_PAUSED) + return DMA_PAUSED; /* Whether waiting or running, we're in progress */ - return ret; + return DMA_IN_PROGRESS; } /* PrimeCell DMA extension */ @@ -1280,14 +1080,38 @@ static u32 pl08x_burst(u32 maxburst) return burst_sizes[i].reg; } -static u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan, - enum dma_slave_buswidth addr_width, u32 maxburst) +static int dma_set_runtime_config(struct dma_chan *chan, + struct dma_slave_config *config) { - u32 width, burst, cctl = 0; + struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); + struct pl08x_driver_data *pl08x = plchan->host; + enum dma_slave_buswidth addr_width; + u32 width, burst, maxburst; + u32 cctl = 0; + + if (!plchan->slave) + return -EINVAL; + + /* Transfer direction */ + plchan->runtime_direction = config->direction; + if (config->direction == DMA_MEM_TO_DEV) { + addr_width = config->dst_addr_width; + maxburst = config->dst_maxburst; + } else if (config->direction == DMA_DEV_TO_MEM) { + addr_width = config->src_addr_width; + maxburst = config->src_maxburst; + } else { + dev_err(&pl08x->adev->dev, + "bad runtime_config: alien transfer direction\n"); + return -EINVAL; + } width = pl08x_width(addr_width); - if (width == ~0) - return ~0; + if (width == ~0) { + dev_err(&pl08x->adev->dev, + "bad runtime_config: alien address width\n"); + return -EINVAL; + } cctl |= width << PL080_CONTROL_SWIDTH_SHIFT; cctl |= width << PL080_CONTROL_DWIDTH_SHIFT; @@ -1304,23 +1128,28 @@ static u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan, cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT; cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT; - return pl08x_cctl(cctl); -} - -static int dma_set_runtime_config(struct dma_chan *chan, - struct dma_slave_config *config) -{ - struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); - - if (!plchan->slave) - return -EINVAL; + plchan->device_fc = config->device_fc; - /* Reject definitely invalid configurations */ - if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || - config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) - return -EINVAL; + if (plchan->runtime_direction == DMA_DEV_TO_MEM) { + plchan->src_addr = config->src_addr; + plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR | + pl08x_select_bus(plchan->cd->periph_buses, + pl08x->mem_buses); + } else { + plchan->dst_addr = config->dst_addr; + plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR | + pl08x_select_bus(pl08x->mem_buses, + plchan->cd->periph_buses); + } - plchan->cfg = *config; + dev_dbg(&pl08x->adev->dev, + "configured channel %s (%s) for %s, data width %d, " + "maxburst %d words, LE, CCTL=0x%08x\n", + dma_chan_name(chan), plchan->name, + (config->direction == DMA_DEV_TO_MEM) ? "RX" : "TX", + addr_width, + maxburst, + cctl); return 0; } @@ -1334,19 +1163,95 @@ static void pl08x_issue_pending(struct dma_chan *chan) struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); unsigned long flags; - spin_lock_irqsave(&plchan->vc.lock, flags); - if (vchan_issue_pending(&plchan->vc)) { - if (!plchan->phychan && plchan->state != PL08X_CHAN_WAITING) - pl08x_phy_alloc_and_start(plchan); + spin_lock_irqsave(&plchan->lock, flags); + /* Something is already active, or we're waiting for a channel... */ + if (plchan->at || plchan->state == PL08X_CHAN_WAITING) { + spin_unlock_irqrestore(&plchan->lock, flags); + return; + } + + /* Take the first element in the queue and execute it */ + if (!list_empty(&plchan->pend_list)) { + struct pl08x_txd *next; + + next = list_first_entry(&plchan->pend_list, + struct pl08x_txd, + node); + list_del(&next->node); + plchan->state = PL08X_CHAN_RUNNING; + + pl08x_start_txd(plchan, next); } - spin_unlock_irqrestore(&plchan->vc.lock, flags); + + spin_unlock_irqrestore(&plchan->lock, flags); +} + +static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan, + struct pl08x_txd *txd) +{ + struct pl08x_driver_data *pl08x = plchan->host; + unsigned long flags; + int num_llis, ret; + + num_llis = pl08x_fill_llis_for_desc(pl08x, txd); + if (!num_llis) { + spin_lock_irqsave(&plchan->lock, flags); + pl08x_free_txd(pl08x, txd); + spin_unlock_irqrestore(&plchan->lock, flags); + return -EINVAL; + } + + spin_lock_irqsave(&plchan->lock, flags); + + /* + * See if we already have a physical channel allocated, + * else this is the time to try to get one. + */ + ret = prep_phy_channel(plchan, txd); + if (ret) { + /* + * No physical channel was available. + * + * memcpy transfers can be sorted out at submission time. + * + * Slave transfers may have been denied due to platform + * channel muxing restrictions. Since there is no guarantee + * that this will ever be resolved, and the signal must be + * acquired AFTER acquiring the physical channel, we will let + * them be NACK:ed with -EBUSY here. The drivers can retry + * the prep() call if they are eager on doing this using DMA. + */ + if (plchan->slave) { + pl08x_free_txd_list(pl08x, plchan); + pl08x_free_txd(pl08x, txd); + spin_unlock_irqrestore(&plchan->lock, flags); + return -EBUSY; + } + } else + /* + * Else we're all set, paused and ready to roll, status + * will switch to PL08X_CHAN_RUNNING when we call + * issue_pending(). If there is something running on the + * channel already we don't change its state. + */ + if (plchan->state == PL08X_CHAN_IDLE) + plchan->state = PL08X_CHAN_PAUSED; + + spin_unlock_irqrestore(&plchan->lock, flags); + + return 0; } -static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan) +static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan, + unsigned long flags) { struct pl08x_txd *txd = kzalloc(sizeof(*txd), GFP_NOWAIT); if (txd) { + dma_async_tx_descriptor_init(&txd->tx, &plchan->chan); + txd->tx.flags = flags; + txd->tx.tx_submit = pl08x_tx_submit; + INIT_LIST_HEAD(&txd->node); INIT_LIST_HEAD(&txd->dsg_list); /* Always enable error and terminal interrupts */ @@ -1369,7 +1274,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( struct pl08x_sg *dsg; int ret; - txd = pl08x_get_txd(plchan); + txd = pl08x_get_txd(plchan, flags); if (!txd) { dev_err(&pl08x->adev->dev, "%s no memory for descriptor\n", __func__); @@ -1385,13 +1290,14 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( } list_add_tail(&dsg->node, &txd->dsg_list); + txd->direction = DMA_NONE; dsg->src_addr = src; dsg->dst_addr = dest; dsg->len = len; /* Set platform data for m2m */ txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; - txd->cctl = pl08x->pd->memcpy_channel.cctl_memcpy & + txd->cctl = pl08x->pd->memcpy_channel.cctl & ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2); /* Both to be incremented or the code will break */ @@ -1401,13 +1307,11 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( txd->cctl |= pl08x_select_bus(pl08x->mem_buses, pl08x->mem_buses); - ret = pl08x_fill_llis_for_desc(plchan->host, txd); - if (!ret) { - pl08x_free_txd(pl08x, txd); + ret = pl08x_prep_channel_resources(plchan, txd); + if (ret) return NULL; - } - return vchan_tx_prep(&plchan->vc, &txd->vd, flags); + return &txd->tx; } static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( @@ -1420,40 +1324,36 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( struct pl08x_txd *txd; struct pl08x_sg *dsg; struct scatterlist *sg; - enum dma_slave_buswidth addr_width; dma_addr_t slave_addr; int ret, tmp; - u8 src_buses, dst_buses; - u32 maxburst, cctl; dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n", __func__, sg_dma_len(sgl), plchan->name); - txd = pl08x_get_txd(plchan); + txd = pl08x_get_txd(plchan, flags); if (!txd) { dev_err(&pl08x->adev->dev, "%s no txd\n", __func__); return NULL; } + if (direction != plchan->runtime_direction) + dev_err(&pl08x->adev->dev, "%s DMA setup does not match " + "the direction configured for the PrimeCell\n", + __func__); + /* * Set up addresses, the PrimeCell configured address * will take precedence since this may configure the * channel target address dynamically at runtime. */ + txd->direction = direction; + if (direction == DMA_MEM_TO_DEV) { - cctl = PL080_CONTROL_SRC_INCR; - slave_addr = plchan->cfg.dst_addr; - addr_width = plchan->cfg.dst_addr_width; - maxburst = plchan->cfg.dst_maxburst; - src_buses = pl08x->mem_buses; - dst_buses = plchan->cd->periph_buses; + txd->cctl = plchan->dst_cctl; + slave_addr = plchan->dst_addr; } else if (direction == DMA_DEV_TO_MEM) { - cctl = PL080_CONTROL_DST_INCR; - slave_addr = plchan->cfg.src_addr; - addr_width = plchan->cfg.src_addr_width; - maxburst = plchan->cfg.src_maxburst; - src_buses = plchan->cd->periph_buses; - dst_buses = pl08x->mem_buses; + txd->cctl = plchan->src_cctl; + slave_addr = plchan->src_addr; } else { pl08x_free_txd(pl08x, txd); dev_err(&pl08x->adev->dev, @@ -1461,17 +1361,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( return NULL; } - cctl |= pl08x_get_cctl(plchan, addr_width, maxburst); - if (cctl == ~0) { - pl08x_free_txd(pl08x, txd); - dev_err(&pl08x->adev->dev, - "DMA slave configuration botched?\n"); - return NULL; - } - - txd->cctl = cctl | pl08x_select_bus(src_buses, dst_buses); - - if (plchan->cfg.device_fc) + if (plchan->device_fc) tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER_PER : PL080_FLOW_PER2MEM_PER; else @@ -1480,28 +1370,9 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT; - ret = pl08x_request_mux(plchan); - if (ret < 0) { - pl08x_free_txd(pl08x, txd); - dev_dbg(&pl08x->adev->dev, - "unable to mux for transfer on %s due to platform restrictions\n", - plchan->name); - return NULL; - } - - dev_dbg(&pl08x->adev->dev, "allocated DMA request signal %d for xfer on %s\n", - plchan->signal, plchan->name); - - /* Assign the flow control signal to this channel */ - if (direction == DMA_MEM_TO_DEV) - txd->ccfg |= plchan->signal << PL080_CONFIG_DST_SEL_SHIFT; - else - txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT; - for_each_sg(sgl, sg, sg_len, tmp) { dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT); if (!dsg) { - pl08x_release_mux(plchan); pl08x_free_txd(pl08x, txd); dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n", __func__); @@ -1519,14 +1390,11 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( } } - ret = pl08x_fill_llis_for_desc(plchan->host, txd); - if (!ret) { - pl08x_release_mux(plchan); - pl08x_free_txd(pl08x, txd); + ret = pl08x_prep_channel_resources(plchan, txd); + if (ret) return NULL; - } - return vchan_tx_prep(&plchan->vc, &txd->vd, flags); + return &txd->tx; } static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, @@ -1547,9 +1415,9 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, * Anything succeeds on channels with no physical allocation and * no queued transfers. */ - spin_lock_irqsave(&plchan->vc.lock, flags); + spin_lock_irqsave(&plchan->lock, flags); if (!plchan->phychan && !plchan->at) { - spin_unlock_irqrestore(&plchan->vc.lock, flags); + spin_unlock_irqrestore(&plchan->lock, flags); return 0; } @@ -1558,15 +1426,18 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, plchan->state = PL08X_CHAN_IDLE; if (plchan->phychan) { + pl08x_terminate_phy_chan(pl08x, plchan->phychan); + /* * Mark physical channel as free and free any slave * signal */ - pl08x_phy_free(plchan); + release_phy_channel(plchan); + plchan->phychan_hold = 0; } /* Dequeue jobs and free LLIs */ if (plchan->at) { - pl08x_desc_free(&plchan->at->vd); + pl08x_free_txd(pl08x, plchan->at); plchan->at = NULL; } /* Dequeue jobs not yet fired as well */ @@ -1586,7 +1457,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, break; } - spin_unlock_irqrestore(&plchan->vc.lock, flags); + spin_unlock_irqrestore(&plchan->lock, flags); return ret; } @@ -1623,6 +1494,123 @@ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x) writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG); } +static void pl08x_unmap_buffers(struct pl08x_txd *txd) +{ + struct device *dev = txd->tx.chan->device->dev; + struct pl08x_sg *dsg; + + if (!(txd->tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + if (txd->tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE) + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_single(dev, dsg->src_addr, dsg->len, + DMA_TO_DEVICE); + else { + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_page(dev, dsg->src_addr, dsg->len, + DMA_TO_DEVICE); + } + } + if (!(txd->tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (txd->tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE) + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_single(dev, dsg->dst_addr, dsg->len, + DMA_FROM_DEVICE); + else + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_page(dev, dsg->dst_addr, dsg->len, + DMA_FROM_DEVICE); + } +} + +static void pl08x_tasklet(unsigned long data) +{ + struct pl08x_dma_chan *plchan = (struct pl08x_dma_chan *) data; + struct pl08x_driver_data *pl08x = plchan->host; + struct pl08x_txd *txd; + unsigned long flags; + + spin_lock_irqsave(&plchan->lock, flags); + + txd = plchan->at; + plchan->at = NULL; + + if (txd) { + /* Update last completed */ + dma_cookie_complete(&txd->tx); + } + + /* If a new descriptor is queued, set it up plchan->at is NULL here */ + if (!list_empty(&plchan->pend_list)) { + struct pl08x_txd *next; + + next = list_first_entry(&plchan->pend_list, + struct pl08x_txd, + node); + list_del(&next->node); + + pl08x_start_txd(plchan, next); + } else if (plchan->phychan_hold) { + /* + * This channel is still in use - we have a new txd being + * prepared and will soon be queued. Don't give up the + * physical channel. + */ + } else { + struct pl08x_dma_chan *waiting = NULL; + + /* + * No more jobs, so free up the physical channel + * Free any allocated signal on slave transfers too + */ + release_phy_channel(plchan); + plchan->state = PL08X_CHAN_IDLE; + + /* + * And NOW before anyone else can grab that free:d up + * physical channel, see if there is some memcpy pending + * that seriously needs to start because of being stacked + * up while we were choking the physical channels with data. + */ + list_for_each_entry(waiting, &pl08x->memcpy.channels, + chan.device_node) { + if (waiting->state == PL08X_CHAN_WAITING && + waiting->waiting != NULL) { + int ret; + + /* This should REALLY not fail now */ + ret = prep_phy_channel(waiting, + waiting->waiting); + BUG_ON(ret); + waiting->phychan_hold--; + waiting->state = PL08X_CHAN_RUNNING; + waiting->waiting = NULL; + pl08x_issue_pending(&waiting->chan); + break; + } + } + } + + spin_unlock_irqrestore(&plchan->lock, flags); + + if (txd) { + dma_async_tx_callback callback = txd->tx.callback; + void *callback_param = txd->tx.callback_param; + + /* Don't try to unmap buffers on slave channels */ + if (!plchan->slave) + pl08x_unmap_buffers(txd); + + /* Free the descriptor */ + spin_lock_irqsave(&plchan->lock, flags); + pl08x_free_txd(pl08x, txd); + spin_unlock_irqrestore(&plchan->lock, flags); + + /* Callback to signal completion */ + if (callback) + callback(callback_param); + } +} + static irqreturn_t pl08x_irq(int irq, void *dev) { struct pl08x_driver_data *pl08x = dev; @@ -1647,7 +1635,6 @@ static irqreturn_t pl08x_irq(int irq, void *dev) /* Locate physical channel */ struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i]; struct pl08x_dma_chan *plchan = phychan->serving; - struct pl08x_txd *tx; if (!plchan) { dev_err(&pl08x->adev->dev, @@ -1656,29 +1643,8 @@ static irqreturn_t pl08x_irq(int irq, void *dev) continue; } - spin_lock(&plchan->vc.lock); - tx = plchan->at; - if (tx) { - plchan->at = NULL; - /* - * This descriptor is done, release its mux - * reservation. - */ - pl08x_release_mux(plchan); - tx->done = true; - vchan_cookie_complete(&tx->vd); - - /* - * And start the next descriptor (if any), - * otherwise free this channel. - */ - if (vchan_next_desc(&plchan->vc)) - pl08x_start_next_txd(plchan); - else - pl08x_phy_free(plchan); - } - spin_unlock(&plchan->vc.lock); - + /* Schedule tasklet on this channel */ + tasklet_schedule(&plchan->tasklet); mask |= (1 << i); } } @@ -1688,10 +1654,16 @@ static irqreturn_t pl08x_irq(int irq, void *dev) static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan) { + u32 cctl = pl08x_cctl(chan->cd->cctl); + chan->slave = true; chan->name = chan->cd->bus_id; - chan->cfg.src_addr = chan->cd->addr; - chan->cfg.dst_addr = chan->cd->addr; + chan->src_addr = chan->cd->addr; + chan->dst_addr = chan->cd->addr; + chan->src_cctl = cctl | PL080_CONTROL_DST_INCR | + pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses); + chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR | + pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses); } /* @@ -1721,7 +1693,6 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, chan->host = pl08x; chan->state = PL08X_CHAN_IDLE; - chan->signal = -1; if (slave) { chan->cd = &pl08x->pd->slave_channels[i]; @@ -1734,12 +1705,26 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, return -ENOMEM; } } + if (chan->cd->circular_buffer) { + dev_err(&pl08x->adev->dev, + "channel %s: circular buffers not supported\n", + chan->name); + kfree(chan); + continue; + } dev_dbg(&pl08x->adev->dev, "initialize virtual channel \"%s\"\n", chan->name); - chan->vc.desc_free = pl08x_desc_free; - vchan_init(&chan->vc, dmadev); + chan->chan.device = dmadev; + dma_cookie_init(&chan->chan); + + spin_lock_init(&chan->lock); + INIT_LIST_HEAD(&chan->pend_list); + tasklet_init(&chan->tasklet, pl08x_tasklet, + (unsigned long) chan); + + list_add_tail(&chan->chan.device_node, &dmadev->channels); } dev_info(&pl08x->adev->dev, "initialized %d virtual %s channels\n", i, slave ? "slave" : "memcpy"); @@ -1752,8 +1737,8 @@ static void pl08x_free_virtual_channels(struct dma_device *dmadev) struct pl08x_dma_chan *next; list_for_each_entry_safe(chan, - next, &dmadev->channels, vc.chan.device_node) { - list_del(&chan->vc.chan.device_node); + next, &dmadev->channels, chan.device_node) { + list_del(&chan->chan.device_node); kfree(chan); } } @@ -1806,7 +1791,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data) seq_printf(s, "\nPL08x virtual memcpy channels:\n"); seq_printf(s, "CHANNEL:\tSTATE:\n"); seq_printf(s, "--------\t------\n"); - list_for_each_entry(chan, &pl08x->memcpy.channels, vc.chan.device_node) { + list_for_each_entry(chan, &pl08x->memcpy.channels, chan.device_node) { seq_printf(s, "%s\t\t%s\n", chan->name, pl08x_state_str(chan->state)); } @@ -1814,7 +1799,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data) seq_printf(s, "\nPL08x virtual slave channels:\n"); seq_printf(s, "CHANNEL:\tSTATE:\n"); seq_printf(s, "--------\t------\n"); - list_for_each_entry(chan, &pl08x->slave.channels, vc.chan.device_node) { + list_for_each_entry(chan, &pl08x->slave.channels, chan.device_node) { seq_printf(s, "%s\t\t%s\n", chan->name, pl08x_state_str(chan->state)); } @@ -1866,6 +1851,9 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) goto out_no_pl08x; } + pm_runtime_set_active(&adev->dev); + pm_runtime_enable(&adev->dev); + /* Initialize memcpy engine */ dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask); pl08x->memcpy.dev = &adev->dev; @@ -1915,6 +1903,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) goto out_no_lli_pool; } + spin_lock_init(&pl08x->lock); + pl08x->base = ioremap(adev->res.start, resource_size(&adev->res)); if (!pl08x->base) { ret = -ENOMEM; @@ -1952,6 +1942,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) ch->id = i; ch->base = pl08x->base + PL080_Cx_BASE(i); spin_lock_init(&ch->lock); + ch->signal = -1; /* * Nomadik variants can have channels that are locked @@ -2016,6 +2007,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) amba_part(adev), amba_rev(adev), (unsigned long long)adev->res.start, adev->irq[0]); + pm_runtime_put(&adev->dev); return 0; out_no_slave_reg: @@ -2034,6 +2026,9 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) dma_pool_destroy(pl08x->pool); out_no_lli_pool: out_no_platdata: + pm_runtime_put(&adev->dev); + pm_runtime_disable(&adev->dev); + kfree(pl08x); out_no_pl08x: amba_release_regions(adev); diff --git a/trunk/drivers/dma/omap-dma.c b/trunk/drivers/dma/omap-dma.c deleted file mode 100644 index ae0561826137..000000000000 --- a/trunk/drivers/dma/omap-dma.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * OMAP DMAengine support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "virt-dma.h" -#include - -struct omap_dmadev { - struct dma_device ddev; - spinlock_t lock; - struct tasklet_struct task; - struct list_head pending; -}; - -struct omap_chan { - struct virt_dma_chan vc; - struct list_head node; - - struct dma_slave_config cfg; - unsigned dma_sig; - bool cyclic; - - int dma_ch; - struct omap_desc *desc; - unsigned sgidx; -}; - -struct omap_sg { - dma_addr_t addr; - uint32_t en; /* number of elements (24-bit) */ - uint32_t fn; /* number of frames (16-bit) */ -}; - -struct omap_desc { - struct virt_dma_desc vd; - enum dma_transfer_direction dir; - dma_addr_t dev_addr; - - int16_t fi; /* for OMAP_DMA_SYNC_PACKET */ - uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */ - uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */ - uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */ - uint8_t periph_port; /* Peripheral port */ - - unsigned sglen; - struct omap_sg sg[0]; -}; - -static const unsigned es_bytes[] = { - [OMAP_DMA_DATA_TYPE_S8] = 1, - [OMAP_DMA_DATA_TYPE_S16] = 2, - [OMAP_DMA_DATA_TYPE_S32] = 4, -}; - -static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d) -{ - return container_of(d, struct omap_dmadev, ddev); -} - -static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c) -{ - return container_of(c, struct omap_chan, vc.chan); -} - -static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t) -{ - return container_of(t, struct omap_desc, vd.tx); -} - -static void omap_dma_desc_free(struct virt_dma_desc *vd) -{ - kfree(container_of(vd, struct omap_desc, vd)); -} - -static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d, - unsigned idx) -{ - struct omap_sg *sg = d->sg + idx; - - if (d->dir == DMA_DEV_TO_MEM) - omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF, - OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0); - else - omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF, - OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0); - - omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn, - d->sync_mode, c->dma_sig, d->sync_type); - - omap_start_dma(c->dma_ch); -} - -static void omap_dma_start_desc(struct omap_chan *c) -{ - struct virt_dma_desc *vd = vchan_next_desc(&c->vc); - struct omap_desc *d; - - if (!vd) { - c->desc = NULL; - return; - } - - list_del(&vd->node); - - c->desc = d = to_omap_dma_desc(&vd->tx); - c->sgidx = 0; - - if (d->dir == DMA_DEV_TO_MEM) - omap_set_dma_src_params(c->dma_ch, d->periph_port, - OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi); - else - omap_set_dma_dest_params(c->dma_ch, d->periph_port, - OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi); - - omap_dma_start_sg(c, d, 0); -} - -static void omap_dma_callback(int ch, u16 status, void *data) -{ - struct omap_chan *c = data; - struct omap_desc *d; - unsigned long flags; - - spin_lock_irqsave(&c->vc.lock, flags); - d = c->desc; - if (d) { - if (!c->cyclic) { - if (++c->sgidx < d->sglen) { - omap_dma_start_sg(c, d, c->sgidx); - } else { - omap_dma_start_desc(c); - vchan_cookie_complete(&d->vd); - } - } else { - vchan_cyclic_callback(&d->vd); - } - } - spin_unlock_irqrestore(&c->vc.lock, flags); -} - -/* - * This callback schedules all pending channels. We could be more - * clever here by postponing allocation of the real DMA channels to - * this point, and freeing them when our virtual channel becomes idle. - * - * We would then need to deal with 'all channels in-use' - */ -static void omap_dma_sched(unsigned long data) -{ - struct omap_dmadev *d = (struct omap_dmadev *)data; - LIST_HEAD(head); - - spin_lock_irq(&d->lock); - list_splice_tail_init(&d->pending, &head); - spin_unlock_irq(&d->lock); - - while (!list_empty(&head)) { - struct omap_chan *c = list_first_entry(&head, - struct omap_chan, node); - - spin_lock_irq(&c->vc.lock); - list_del_init(&c->node); - omap_dma_start_desc(c); - spin_unlock_irq(&c->vc.lock); - } -} - -static int omap_dma_alloc_chan_resources(struct dma_chan *chan) -{ - struct omap_chan *c = to_omap_dma_chan(chan); - - dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig); - - return omap_request_dma(c->dma_sig, "DMA engine", - omap_dma_callback, c, &c->dma_ch); -} - -static void omap_dma_free_chan_resources(struct dma_chan *chan) -{ - struct omap_chan *c = to_omap_dma_chan(chan); - - vchan_free_chan_resources(&c->vc); - omap_free_dma(c->dma_ch); - - dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig); -} - -static size_t omap_dma_sg_size(struct omap_sg *sg) -{ - return sg->en * sg->fn; -} - -static size_t omap_dma_desc_size(struct omap_desc *d) -{ - unsigned i; - size_t size; - - for (size = i = 0; i < d->sglen; i++) - size += omap_dma_sg_size(&d->sg[i]); - - return size * es_bytes[d->es]; -} - -static size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr) -{ - unsigned i; - size_t size, es_size = es_bytes[d->es]; - - for (size = i = 0; i < d->sglen; i++) { - size_t this_size = omap_dma_sg_size(&d->sg[i]) * es_size; - - if (size) - size += this_size; - else if (addr >= d->sg[i].addr && - addr < d->sg[i].addr + this_size) - size += d->sg[i].addr + this_size - addr; - } - return size; -} - -static enum dma_status omap_dma_tx_status(struct dma_chan *chan, - dma_cookie_t cookie, struct dma_tx_state *txstate) -{ - struct omap_chan *c = to_omap_dma_chan(chan); - struct virt_dma_desc *vd; - enum dma_status ret; - unsigned long flags; - - ret = dma_cookie_status(chan, cookie, txstate); - if (ret == DMA_SUCCESS || !txstate) - return ret; - - spin_lock_irqsave(&c->vc.lock, flags); - vd = vchan_find_desc(&c->vc, cookie); - if (vd) { - txstate->residue = omap_dma_desc_size(to_omap_dma_desc(&vd->tx)); - } else if (c->desc && c->desc->vd.tx.cookie == cookie) { - struct omap_desc *d = c->desc; - dma_addr_t pos; - - if (d->dir == DMA_MEM_TO_DEV) - pos = omap_get_dma_src_pos(c->dma_ch); - else if (d->dir == DMA_DEV_TO_MEM) - pos = omap_get_dma_dst_pos(c->dma_ch); - else - pos = 0; - - txstate->residue = omap_dma_desc_size_pos(d, pos); - } else { - txstate->residue = 0; - } - spin_unlock_irqrestore(&c->vc.lock, flags); - - return ret; -} - -static void omap_dma_issue_pending(struct dma_chan *chan) -{ - struct omap_chan *c = to_omap_dma_chan(chan); - unsigned long flags; - - spin_lock_irqsave(&c->vc.lock, flags); - if (vchan_issue_pending(&c->vc) && !c->desc) { - struct omap_dmadev *d = to_omap_dma_dev(chan->device); - spin_lock(&d->lock); - if (list_empty(&c->node)) - list_add_tail(&c->node, &d->pending); - spin_unlock(&d->lock); - tasklet_schedule(&d->task); - } - spin_unlock_irqrestore(&c->vc.lock, flags); -} - -static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg( - struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen, - enum dma_transfer_direction dir, unsigned long tx_flags, void *context) -{ - struct omap_chan *c = to_omap_dma_chan(chan); - enum dma_slave_buswidth dev_width; - struct scatterlist *sgent; - struct omap_desc *d; - dma_addr_t dev_addr; - unsigned i, j = 0, es, en, frame_bytes, sync_type; - u32 burst; - - if (dir == DMA_DEV_TO_MEM) { - dev_addr = c->cfg.src_addr; - dev_width = c->cfg.src_addr_width; - burst = c->cfg.src_maxburst; - sync_type = OMAP_DMA_SRC_SYNC; - } else if (dir == DMA_MEM_TO_DEV) { - dev_addr = c->cfg.dst_addr; - dev_width = c->cfg.dst_addr_width; - burst = c->cfg.dst_maxburst; - sync_type = OMAP_DMA_DST_SYNC; - } else { - dev_err(chan->device->dev, "%s: bad direction?\n", __func__); - return NULL; - } - - /* Bus width translates to the element size (ES) */ - switch (dev_width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - es = OMAP_DMA_DATA_TYPE_S8; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - es = OMAP_DMA_DATA_TYPE_S16; - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - es = OMAP_DMA_DATA_TYPE_S32; - break; - default: /* not reached */ - return NULL; - } - - /* Now allocate and setup the descriptor. */ - d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC); - if (!d) - return NULL; - - d->dir = dir; - d->dev_addr = dev_addr; - d->es = es; - d->sync_mode = OMAP_DMA_SYNC_FRAME; - d->sync_type = sync_type; - d->periph_port = OMAP_DMA_PORT_TIPB; - - /* - * Build our scatterlist entries: each contains the address, - * the number of elements (EN) in each frame, and the number of - * frames (FN). Number of bytes for this entry = ES * EN * FN. - * - * Burst size translates to number of elements with frame sync. - * Note: DMA engine defines burst to be the number of dev-width - * transfers. - */ - en = burst; - frame_bytes = es_bytes[es] * en; - for_each_sg(sgl, sgent, sglen, i) { - d->sg[j].addr = sg_dma_address(sgent); - d->sg[j].en = en; - d->sg[j].fn = sg_dma_len(sgent) / frame_bytes; - j++; - } - - d->sglen = j; - - return vchan_tx_prep(&c->vc, &d->vd, tx_flags); -} - -static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( - struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction dir, void *context) -{ - struct omap_chan *c = to_omap_dma_chan(chan); - enum dma_slave_buswidth dev_width; - struct omap_desc *d; - dma_addr_t dev_addr; - unsigned es, sync_type; - u32 burst; - - if (dir == DMA_DEV_TO_MEM) { - dev_addr = c->cfg.src_addr; - dev_width = c->cfg.src_addr_width; - burst = c->cfg.src_maxburst; - sync_type = OMAP_DMA_SRC_SYNC; - } else if (dir == DMA_MEM_TO_DEV) { - dev_addr = c->cfg.dst_addr; - dev_width = c->cfg.dst_addr_width; - burst = c->cfg.dst_maxburst; - sync_type = OMAP_DMA_DST_SYNC; - } else { - dev_err(chan->device->dev, "%s: bad direction?\n", __func__); - return NULL; - } - - /* Bus width translates to the element size (ES) */ - switch (dev_width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - es = OMAP_DMA_DATA_TYPE_S8; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - es = OMAP_DMA_DATA_TYPE_S16; - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - es = OMAP_DMA_DATA_TYPE_S32; - break; - default: /* not reached */ - return NULL; - } - - /* Now allocate and setup the descriptor. */ - d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC); - if (!d) - return NULL; - - d->dir = dir; - d->dev_addr = dev_addr; - d->fi = burst; - d->es = es; - d->sync_mode = OMAP_DMA_SYNC_PACKET; - d->sync_type = sync_type; - d->periph_port = OMAP_DMA_PORT_MPUI; - d->sg[0].addr = buf_addr; - d->sg[0].en = period_len / es_bytes[es]; - d->sg[0].fn = buf_len / period_len; - d->sglen = 1; - - if (!c->cyclic) { - c->cyclic = true; - omap_dma_link_lch(c->dma_ch, c->dma_ch); - omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ); - omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ); - } - - if (!cpu_class_is_omap1()) { - omap_set_dma_src_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16); - omap_set_dma_dest_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16); - } - - return vchan_tx_prep(&c->vc, &d->vd, DMA_CTRL_ACK | DMA_PREP_INTERRUPT); -} - -static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg) -{ - if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || - cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) - return -EINVAL; - - memcpy(&c->cfg, cfg, sizeof(c->cfg)); - - return 0; -} - -static int omap_dma_terminate_all(struct omap_chan *c) -{ - struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device); - unsigned long flags; - LIST_HEAD(head); - - spin_lock_irqsave(&c->vc.lock, flags); - - /* Prevent this channel being scheduled */ - spin_lock(&d->lock); - list_del_init(&c->node); - spin_unlock(&d->lock); - - /* - * Stop DMA activity: we assume the callback will not be called - * after omap_stop_dma() returns (even if it does, it will see - * c->desc is NULL and exit.) - */ - if (c->desc) { - c->desc = NULL; - omap_stop_dma(c->dma_ch); - } - - if (c->cyclic) { - c->cyclic = false; - omap_dma_unlink_lch(c->dma_ch, c->dma_ch); - } - - vchan_get_all_descriptors(&c->vc, &head); - spin_unlock_irqrestore(&c->vc.lock, flags); - vchan_dma_desc_free_list(&c->vc, &head); - - return 0; -} - -static int omap_dma_pause(struct omap_chan *c) -{ - /* FIXME: not supported by platform private API */ - return -EINVAL; -} - -static int omap_dma_resume(struct omap_chan *c) -{ - /* FIXME: not supported by platform private API */ - return -EINVAL; -} - -static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct omap_chan *c = to_omap_dma_chan(chan); - int ret; - - switch (cmd) { - case DMA_SLAVE_CONFIG: - ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg); - break; - - case DMA_TERMINATE_ALL: - ret = omap_dma_terminate_all(c); - break; - - case DMA_PAUSE: - ret = omap_dma_pause(c); - break; - - case DMA_RESUME: - ret = omap_dma_resume(c); - break; - - default: - ret = -ENXIO; - break; - } - - return ret; -} - -static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig) -{ - struct omap_chan *c; - - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return -ENOMEM; - - c->dma_sig = dma_sig; - c->vc.desc_free = omap_dma_desc_free; - vchan_init(&c->vc, &od->ddev); - INIT_LIST_HEAD(&c->node); - - od->ddev.chancnt++; - - return 0; -} - -static void omap_dma_free(struct omap_dmadev *od) -{ - tasklet_kill(&od->task); - while (!list_empty(&od->ddev.channels)) { - struct omap_chan *c = list_first_entry(&od->ddev.channels, - struct omap_chan, vc.chan.device_node); - - list_del(&c->vc.chan.device_node); - tasklet_kill(&c->vc.task); - kfree(c); - } - kfree(od); -} - -static int omap_dma_probe(struct platform_device *pdev) -{ - struct omap_dmadev *od; - int rc, i; - - od = kzalloc(sizeof(*od), GFP_KERNEL); - if (!od) - return -ENOMEM; - - dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); - dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); - od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources; - od->ddev.device_free_chan_resources = omap_dma_free_chan_resources; - od->ddev.device_tx_status = omap_dma_tx_status; - od->ddev.device_issue_pending = omap_dma_issue_pending; - od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg; - od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic; - od->ddev.device_control = omap_dma_control; - od->ddev.dev = &pdev->dev; - INIT_LIST_HEAD(&od->ddev.channels); - INIT_LIST_HEAD(&od->pending); - spin_lock_init(&od->lock); - - tasklet_init(&od->task, omap_dma_sched, (unsigned long)od); - - for (i = 0; i < 127; i++) { - rc = omap_dma_chan_init(od, i); - if (rc) { - omap_dma_free(od); - return rc; - } - } - - rc = dma_async_device_register(&od->ddev); - if (rc) { - pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n", - rc); - omap_dma_free(od); - } else { - platform_set_drvdata(pdev, od); - } - - dev_info(&pdev->dev, "OMAP DMA engine driver\n"); - - return rc; -} - -static int omap_dma_remove(struct platform_device *pdev) -{ - struct omap_dmadev *od = platform_get_drvdata(pdev); - - dma_async_device_unregister(&od->ddev); - omap_dma_free(od); - - return 0; -} - -static struct platform_driver omap_dma_driver = { - .probe = omap_dma_probe, - .remove = omap_dma_remove, - .driver = { - .name = "omap-dma-engine", - .owner = THIS_MODULE, - }, -}; - -bool omap_dma_filter_fn(struct dma_chan *chan, void *param) -{ - if (chan->device->dev->driver == &omap_dma_driver.driver) { - struct omap_chan *c = to_omap_dma_chan(chan); - unsigned req = *(unsigned *)param; - - return req == c->dma_sig; - } - return false; -} -EXPORT_SYMBOL_GPL(omap_dma_filter_fn); - -static struct platform_device *pdev; - -static const struct platform_device_info omap_dma_dev_info = { - .name = "omap-dma-engine", - .id = -1, - .dma_mask = DMA_BIT_MASK(32), -}; - -static int omap_dma_init(void) -{ - int rc = platform_driver_register(&omap_dma_driver); - - if (rc == 0) { - pdev = platform_device_register_full(&omap_dma_dev_info); - if (IS_ERR(pdev)) { - platform_driver_unregister(&omap_dma_driver); - rc = PTR_ERR(pdev); - } - } - return rc; -} -subsys_initcall(omap_dma_init); - -static void __exit omap_dma_exit(void) -{ - platform_device_unregister(pdev); - platform_driver_unregister(&omap_dma_driver); -} -module_exit(omap_dma_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/dma/sa11x0-dma.c b/trunk/drivers/dma/sa11x0-dma.c index f5a73606217e..ec78ccef9132 100644 --- a/trunk/drivers/dma/sa11x0-dma.c +++ b/trunk/drivers/dma/sa11x0-dma.c @@ -21,8 +21,6 @@ #include #include -#include "virt-dma.h" - #define NR_PHY_CHAN 6 #define DMA_ALIGN 3 #define DMA_MAX_SIZE 0x1fff @@ -74,13 +72,12 @@ struct sa11x0_dma_sg { }; struct sa11x0_dma_desc { - struct virt_dma_desc vd; - + struct dma_async_tx_descriptor tx; u32 ddar; size_t size; - unsigned period; - bool cyclic; + /* maybe protected by c->lock */ + struct list_head node; unsigned sglen; struct sa11x0_dma_sg sg[0]; }; @@ -88,11 +85,15 @@ struct sa11x0_dma_desc { struct sa11x0_dma_phy; struct sa11x0_dma_chan { - struct virt_dma_chan vc; + struct dma_chan chan; + spinlock_t lock; + dma_cookie_t lc; - /* protected by c->vc.lock */ + /* protected by c->lock */ struct sa11x0_dma_phy *phy; enum dma_status status; + struct list_head desc_submitted; + struct list_head desc_issued; /* protected by d->lock */ struct list_head node; @@ -108,7 +109,7 @@ struct sa11x0_dma_phy { struct sa11x0_dma_chan *vchan; - /* Protected by c->vc.lock */ + /* Protected by c->lock */ unsigned sg_load; struct sa11x0_dma_desc *txd_load; unsigned sg_done; @@ -126,12 +127,13 @@ struct sa11x0_dma_dev { spinlock_t lock; struct tasklet_struct task; struct list_head chan_pending; + struct list_head desc_complete; struct sa11x0_dma_phy phy[NR_PHY_CHAN]; }; static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan) { - return container_of(chan, struct sa11x0_dma_chan, vc.chan); + return container_of(chan, struct sa11x0_dma_chan, chan); } static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev) @@ -139,26 +141,27 @@ static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev) return container_of(dmadev, struct sa11x0_dma_dev, slave); } -static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c) +static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx) { - struct virt_dma_desc *vd = vchan_next_desc(&c->vc); - - return vd ? container_of(vd, struct sa11x0_dma_desc, vd) : NULL; + return container_of(tx, struct sa11x0_dma_desc, tx); } -static void sa11x0_dma_free_desc(struct virt_dma_desc *vd) +static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c) { - kfree(container_of(vd, struct sa11x0_dma_desc, vd)); + if (list_empty(&c->desc_issued)) + return NULL; + + return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node); } static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd) { - list_del(&txd->vd.node); + list_del(&txd->node); p->txd_load = txd; p->sg_load = 0; dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n", - p->num, &txd->vd, txd->vd.tx.cookie, txd->ddar); + p->num, txd, txd->tx.cookie, txd->ddar); } static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p, @@ -180,24 +183,19 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p, return; if (p->sg_load == txd->sglen) { - if (!txd->cyclic) { - struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c); + struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c); - /* - * We have reached the end of the current descriptor. - * Peek at the next descriptor, and if compatible with - * the current, start processing it. - */ - if (txn && txn->ddar == txd->ddar) { - txd = txn; - sa11x0_dma_start_desc(p, txn); - } else { - p->txd_load = NULL; - return; - } + /* + * We have reached the end of the current descriptor. + * Peek at the next descriptor, and if compatible with + * the current, start processing it. + */ + if (txn && txn->ddar == txd->ddar) { + txd = txn; + sa11x0_dma_start_desc(p, txn); } else { - /* Cyclic: reset back to beginning */ - p->sg_load = 0; + p->txd_load = NULL; + return; } } @@ -231,21 +229,21 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd = p->txd_done; if (++p->sg_done == txd->sglen) { - if (!txd->cyclic) { - vchan_cookie_complete(&txd->vd); + struct sa11x0_dma_dev *d = p->dev; - p->sg_done = 0; - p->txd_done = p->txd_load; + dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n", + p->num, p->txd_done, p->txd_done->tx.cookie); - if (!p->txd_done) - tasklet_schedule(&p->dev->task); - } else { - if ((p->sg_done % txd->period) == 0) - vchan_cyclic_callback(&txd->vd); + c->lc = txd->tx.cookie; - /* Cyclic: reset back to beginning */ - p->sg_done = 0; - } + spin_lock(&d->lock); + list_add_tail(&txd->node, &d->desc_complete); + spin_unlock(&d->lock); + + p->sg_done = 0; + p->txd_done = p->txd_load; + + tasklet_schedule(&d->task); } sa11x0_dma_start_sg(p, c); @@ -282,7 +280,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id) if (c) { unsigned long flags; - spin_lock_irqsave(&c->vc.lock, flags); + spin_lock_irqsave(&c->lock, flags); /* * Now that we're holding the lock, check that the vchan * really is associated with this pchan before touching the @@ -296,7 +294,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id) if (dcsr & DCSR_DONEB) sa11x0_dma_complete(p, c); } - spin_unlock_irqrestore(&c->vc.lock, flags); + spin_unlock_irqrestore(&c->lock, flags); } return IRQ_HANDLED; @@ -334,15 +332,28 @@ static void sa11x0_dma_tasklet(unsigned long arg) struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg; struct sa11x0_dma_phy *p; struct sa11x0_dma_chan *c; + struct sa11x0_dma_desc *txd, *txn; + LIST_HEAD(head); unsigned pch, pch_alloc = 0; dev_dbg(d->slave.dev, "tasklet enter\n"); - list_for_each_entry(c, &d->slave.channels, vc.chan.device_node) { - spin_lock_irq(&c->vc.lock); + /* Get the completed tx descriptors */ + spin_lock_irq(&d->lock); + list_splice_init(&d->desc_complete, &head); + spin_unlock_irq(&d->lock); + + list_for_each_entry(txd, &head, node) { + c = to_sa11x0_dma_chan(txd->tx.chan); + + dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n", + c, txd, txd->tx.cookie); + + spin_lock_irq(&c->lock); p = c->phy; - if (p && !p->txd_done) { - sa11x0_dma_start_txd(c); + if (p) { + if (!p->txd_done) + sa11x0_dma_start_txd(c); if (!p->txd_done) { /* No current txd associated with this channel */ dev_dbg(d->slave.dev, "pchan %u: free\n", p->num); @@ -352,7 +363,7 @@ static void sa11x0_dma_tasklet(unsigned long arg) p->vchan = NULL; } } - spin_unlock_irq(&c->vc.lock); + spin_unlock_irq(&c->lock); } spin_lock_irq(&d->lock); @@ -369,7 +380,7 @@ static void sa11x0_dma_tasklet(unsigned long arg) /* Mark this channel allocated */ p->vchan = c; - dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, &c->vc); + dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c); } } spin_unlock_irq(&d->lock); @@ -379,18 +390,42 @@ static void sa11x0_dma_tasklet(unsigned long arg) p = &d->phy[pch]; c = p->vchan; - spin_lock_irq(&c->vc.lock); + spin_lock_irq(&c->lock); c->phy = p; sa11x0_dma_start_txd(c); - spin_unlock_irq(&c->vc.lock); + spin_unlock_irq(&c->lock); } } + /* Now free the completed tx descriptor, and call their callbacks */ + list_for_each_entry_safe(txd, txn, &head, node) { + dma_async_tx_callback callback = txd->tx.callback; + void *callback_param = txd->tx.callback_param; + + dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n", + txd, txd->tx.cookie); + + kfree(txd); + + if (callback) + callback(callback_param); + } + dev_dbg(d->slave.dev, "tasklet exit\n"); } +static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head) +{ + struct sa11x0_dma_desc *txd, *txn; + + list_for_each_entry_safe(txd, txn, head, node) { + dev_dbg(d->slave.dev, "txd %p: freeing\n", txd); + kfree(txd); + } +} + static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan) { return 0; @@ -401,12 +436,18 @@ static void sa11x0_dma_free_chan_resources(struct dma_chan *chan) struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); unsigned long flags; + LIST_HEAD(head); - spin_lock_irqsave(&d->lock, flags); + spin_lock_irqsave(&c->lock, flags); + spin_lock(&d->lock); list_del_init(&c->node); - spin_unlock_irqrestore(&d->lock, flags); + spin_unlock(&d->lock); + + list_splice_tail_init(&c->desc_submitted, &head); + list_splice_tail_init(&c->desc_issued, &head); + spin_unlock_irqrestore(&c->lock, flags); - vchan_free_chan_resources(&c->vc); + sa11x0_dma_desc_free(d, &head); } static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p) @@ -431,47 +472,33 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan, struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_phy *p; - struct virt_dma_desc *vd; + struct sa11x0_dma_desc *txd; + dma_cookie_t last_used, last_complete; unsigned long flags; enum dma_status ret; + size_t bytes = 0; - ret = dma_cookie_status(&c->vc.chan, cookie, state); - if (ret == DMA_SUCCESS) - return ret; + last_used = c->chan.cookie; + last_complete = c->lc; - if (!state) - return c->status; + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret == DMA_SUCCESS) { + dma_set_tx_state(state, last_complete, last_used, 0); + return ret; + } - spin_lock_irqsave(&c->vc.lock, flags); + spin_lock_irqsave(&c->lock, flags); p = c->phy; + ret = c->status; + if (p) { + dma_addr_t addr = sa11x0_dma_pos(p); - /* - * If the cookie is on our issue queue, then the residue is - * its total size. - */ - vd = vchan_find_desc(&c->vc, cookie); - if (vd) { - state->residue = container_of(vd, struct sa11x0_dma_desc, vd)->size; - } else if (!p) { - state->residue = 0; - } else { - struct sa11x0_dma_desc *txd; - size_t bytes = 0; + dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr); - if (p->txd_done && p->txd_done->vd.tx.cookie == cookie) - txd = p->txd_done; - else if (p->txd_load && p->txd_load->vd.tx.cookie == cookie) - txd = p->txd_load; - else - txd = NULL; - - ret = c->status; + txd = p->txd_done; if (txd) { - dma_addr_t addr = sa11x0_dma_pos(p); unsigned i; - dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr); - for (i = 0; i < txd->sglen; i++) { dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n", i, txd->sg[i].addr, txd->sg[i].len); @@ -494,11 +521,17 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan, bytes += txd->sg[i].len; } } - state->residue = bytes; + if (txd != p->txd_load && p->txd_load) + bytes += p->txd_load->size; + } + list_for_each_entry(txd, &c->desc_issued, node) { + bytes += txd->size; } - spin_unlock_irqrestore(&c->vc.lock, flags); + spin_unlock_irqrestore(&c->lock, flags); + + dma_set_tx_state(state, last_complete, last_used, bytes); - dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", state->residue); + dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes); return ret; } @@ -514,20 +547,40 @@ static void sa11x0_dma_issue_pending(struct dma_chan *chan) struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); unsigned long flags; - spin_lock_irqsave(&c->vc.lock, flags); - if (vchan_issue_pending(&c->vc)) { - if (!c->phy) { - spin_lock(&d->lock); - if (list_empty(&c->node)) { - list_add_tail(&c->node, &d->chan_pending); - tasklet_schedule(&d->task); - dev_dbg(d->slave.dev, "vchan %p: issued\n", &c->vc); - } - spin_unlock(&d->lock); + spin_lock_irqsave(&c->lock, flags); + list_splice_tail_init(&c->desc_submitted, &c->desc_issued); + if (!list_empty(&c->desc_issued)) { + spin_lock(&d->lock); + if (!c->phy && list_empty(&c->node)) { + list_add_tail(&c->node, &d->chan_pending); + tasklet_schedule(&d->task); + dev_dbg(d->slave.dev, "vchan %p: issued\n", c); } + spin_unlock(&d->lock); } else - dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", &c->vc); - spin_unlock_irqrestore(&c->vc.lock, flags); + dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c); + spin_unlock_irqrestore(&c->lock, flags); +} + +static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan); + struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx); + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + c->chan.cookie += 1; + if (c->chan.cookie < 0) + c->chan.cookie = 1; + txd->tx.cookie = c->chan.cookie; + + list_add_tail(&txd->node, &c->desc_submitted); + spin_unlock_irqrestore(&c->lock, flags); + + dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n", + c, txd, txd->tx.cookie); + + return txd->tx.cookie; } static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( @@ -543,7 +596,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( /* SA11x0 channels can only operate in their native direction */ if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) { dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n", - &c->vc, c->ddar, dir); + c, c->ddar, dir); return NULL; } @@ -559,14 +612,14 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1; if (addr & DMA_ALIGN) { dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n", - &c->vc, addr); + c, addr); return NULL; } } txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC); if (!txd) { - dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc); + dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c); return NULL; } @@ -602,73 +655,17 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( } while (len); } + dma_async_tx_descriptor_init(&txd->tx, &c->chan); + txd->tx.flags = flags; + txd->tx.tx_submit = sa11x0_dma_tx_submit; txd->ddar = c->ddar; txd->size = size; txd->sglen = j; dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n", - &c->vc, &txd->vd, txd->size, txd->sglen); + c, txd, txd->size, txd->sglen); - return vchan_tx_prep(&c->vc, &txd->vd, flags); -} - -static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic( - struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period, - enum dma_transfer_direction dir, void *context) -{ - struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); - struct sa11x0_dma_desc *txd; - unsigned i, j, k, sglen, sgperiod; - - /* SA11x0 channels can only operate in their native direction */ - if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) { - dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n", - &c->vc, c->ddar, dir); - return NULL; - } - - sgperiod = DIV_ROUND_UP(period, DMA_MAX_SIZE & ~DMA_ALIGN); - sglen = size * sgperiod / period; - - /* Do not allow zero-sized txds */ - if (sglen == 0) - return NULL; - - txd = kzalloc(sizeof(*txd) + sglen * sizeof(txd->sg[0]), GFP_ATOMIC); - if (!txd) { - dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc); - return NULL; - } - - for (i = k = 0; i < size / period; i++) { - size_t tlen, len = period; - - for (j = 0; j < sgperiod; j++, k++) { - tlen = len; - - if (tlen > DMA_MAX_SIZE) { - unsigned mult = DIV_ROUND_UP(tlen, DMA_MAX_SIZE & ~DMA_ALIGN); - tlen = (tlen / mult) & ~DMA_ALIGN; - } - - txd->sg[k].addr = addr; - txd->sg[k].len = tlen; - addr += tlen; - len -= tlen; - } - - WARN_ON(len != 0); - } - - WARN_ON(k != sglen); - - txd->ddar = c->ddar; - txd->size = size; - txd->sglen = sglen; - txd->cyclic = 1; - txd->period = sgperiod; - - return vchan_tx_prep(&c->vc, &txd->vd, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + return &txd->tx; } static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg) @@ -698,8 +695,8 @@ static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_c if (maxburst == 8) ddar |= DDAR_BS; - dev_dbg(c->vc.chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n", - &c->vc, addr, width, maxburst); + dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n", + c, addr, width, maxburst); c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6; @@ -721,13 +718,16 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg); case DMA_TERMINATE_ALL: - dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc); + dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c); /* Clear the tx descriptor lists */ - spin_lock_irqsave(&c->vc.lock, flags); - vchan_get_all_descriptors(&c->vc, &head); + spin_lock_irqsave(&c->lock, flags); + list_splice_tail_init(&c->desc_submitted, &head); + list_splice_tail_init(&c->desc_issued, &head); p = c->phy; if (p) { + struct sa11x0_dma_desc *txd, *txn; + dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num); /* vchan is assigned to a pchan - stop the channel */ writel(DCSR_RUN | DCSR_IE | @@ -735,13 +735,17 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, DCSR_STRTB | DCSR_DONEB, p->base + DMA_DCSR_C); + list_for_each_entry_safe(txd, txn, &d->desc_complete, node) + if (txd->tx.chan == &c->chan) + list_move(&txd->node, &head); + if (p->txd_load) { if (p->txd_load != p->txd_done) - list_add_tail(&p->txd_load->vd.node, &head); + list_add_tail(&p->txd_load->node, &head); p->txd_load = NULL; } if (p->txd_done) { - list_add_tail(&p->txd_done->vd.node, &head); + list_add_tail(&p->txd_done->node, &head); p->txd_done = NULL; } c->phy = NULL; @@ -750,14 +754,14 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, spin_unlock(&d->lock); tasklet_schedule(&d->task); } - spin_unlock_irqrestore(&c->vc.lock, flags); - vchan_dma_desc_free_list(&c->vc, &head); + spin_unlock_irqrestore(&c->lock, flags); + sa11x0_dma_desc_free(d, &head); ret = 0; break; case DMA_PAUSE: - dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); - spin_lock_irqsave(&c->vc.lock, flags); + dev_dbg(d->slave.dev, "vchan %p: pause\n", c); + spin_lock_irqsave(&c->lock, flags); if (c->status == DMA_IN_PROGRESS) { c->status = DMA_PAUSED; @@ -770,26 +774,26 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, spin_unlock(&d->lock); } } - spin_unlock_irqrestore(&c->vc.lock, flags); + spin_unlock_irqrestore(&c->lock, flags); ret = 0; break; case DMA_RESUME: - dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); - spin_lock_irqsave(&c->vc.lock, flags); + dev_dbg(d->slave.dev, "vchan %p: resume\n", c); + spin_lock_irqsave(&c->lock, flags); if (c->status == DMA_PAUSED) { c->status = DMA_IN_PROGRESS; p = c->phy; if (p) { writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S); - } else if (!list_empty(&c->vc.desc_issued)) { + } else if (!list_empty(&c->desc_issued)) { spin_lock(&d->lock); list_add_tail(&c->node, &d->chan_pending); spin_unlock(&d->lock); } } - spin_unlock_irqrestore(&c->vc.lock, flags); + spin_unlock_irqrestore(&c->lock, flags); ret = 0; break; @@ -849,13 +853,15 @@ static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev, return -ENOMEM; } + c->chan.device = dmadev; c->status = DMA_IN_PROGRESS; c->ddar = chan_desc[i].ddar; c->name = chan_desc[i].name; + spin_lock_init(&c->lock); + INIT_LIST_HEAD(&c->desc_submitted); + INIT_LIST_HEAD(&c->desc_issued); INIT_LIST_HEAD(&c->node); - - c->vc.desc_free = sa11x0_dma_free_desc; - vchan_init(&c->vc, dmadev); + list_add_tail(&c->chan.device_node, &dmadev->channels); } return dma_async_device_register(dmadev); @@ -884,9 +890,8 @@ static void sa11x0_dma_free_channels(struct dma_device *dmadev) { struct sa11x0_dma_chan *c, *cn; - list_for_each_entry_safe(c, cn, &dmadev->channels, vc.chan.device_node) { - list_del(&c->vc.chan.device_node); - tasklet_kill(&c->vc.task); + list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) { + list_del(&c->chan.device_node); kfree(c); } } @@ -910,6 +915,7 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev) spin_lock_init(&d->lock); INIT_LIST_HEAD(&d->chan_pending); + INIT_LIST_HEAD(&d->desc_complete); d->base = ioremap(res->start, resource_size(res)); if (!d->base) { @@ -941,9 +947,7 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev) } dma_cap_set(DMA_SLAVE, d->slave.cap_mask); - dma_cap_set(DMA_CYCLIC, d->slave.cap_mask); d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg; - d->slave.device_prep_dma_cyclic = sa11x0_dma_prep_dma_cyclic; ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev); if (ret) { dev_warn(d->slave.dev, "failed to register slave async device: %d\n", diff --git a/trunk/drivers/dma/virt-dma.c b/trunk/drivers/dma/virt-dma.c deleted file mode 100644 index 6f80432a3f0a..000000000000 --- a/trunk/drivers/dma/virt-dma.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Virtual DMA channel support for DMAengine - * - * Copyright (C) 2012 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include - -#include "virt-dma.h" - -static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx) -{ - return container_of(tx, struct virt_dma_desc, tx); -} - -dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx) -{ - struct virt_dma_chan *vc = to_virt_chan(tx->chan); - struct virt_dma_desc *vd = to_virt_desc(tx); - unsigned long flags; - dma_cookie_t cookie; - - spin_lock_irqsave(&vc->lock, flags); - cookie = dma_cookie_assign(tx); - - list_add_tail(&vd->node, &vc->desc_submitted); - spin_unlock_irqrestore(&vc->lock, flags); - - dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n", - vc, vd, cookie); - - return cookie; -} -EXPORT_SYMBOL_GPL(vchan_tx_submit); - -struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc, - dma_cookie_t cookie) -{ - struct virt_dma_desc *vd; - - list_for_each_entry(vd, &vc->desc_issued, node) - if (vd->tx.cookie == cookie) - return vd; - - return NULL; -} -EXPORT_SYMBOL_GPL(vchan_find_desc); - -/* - * This tasklet handles the completion of a DMA descriptor by - * calling its callback and freeing it. - */ -static void vchan_complete(unsigned long arg) -{ - struct virt_dma_chan *vc = (struct virt_dma_chan *)arg; - struct virt_dma_desc *vd; - dma_async_tx_callback cb = NULL; - void *cb_data = NULL; - LIST_HEAD(head); - - spin_lock_irq(&vc->lock); - list_splice_tail_init(&vc->desc_completed, &head); - vd = vc->cyclic; - if (vd) { - vc->cyclic = NULL; - cb = vd->tx.callback; - cb_data = vd->tx.callback_param; - } - spin_unlock_irq(&vc->lock); - - if (cb) - cb(cb_data); - - while (!list_empty(&head)) { - vd = list_first_entry(&head, struct virt_dma_desc, node); - cb = vd->tx.callback; - cb_data = vd->tx.callback_param; - - list_del(&vd->node); - - vc->desc_free(vd); - - if (cb) - cb(cb_data); - } -} - -void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head) -{ - while (!list_empty(head)) { - struct virt_dma_desc *vd = list_first_entry(head, - struct virt_dma_desc, node); - list_del(&vd->node); - dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd); - vc->desc_free(vd); - } -} -EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list); - -void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev) -{ - dma_cookie_init(&vc->chan); - - spin_lock_init(&vc->lock); - INIT_LIST_HEAD(&vc->desc_submitted); - INIT_LIST_HEAD(&vc->desc_issued); - INIT_LIST_HEAD(&vc->desc_completed); - - tasklet_init(&vc->task, vchan_complete, (unsigned long)vc); - - vc->chan.device = dmadev; - list_add_tail(&vc->chan.device_node, &dmadev->channels); -} -EXPORT_SYMBOL_GPL(vchan_init); - -MODULE_AUTHOR("Russell King"); -MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/dma/virt-dma.h b/trunk/drivers/dma/virt-dma.h deleted file mode 100644 index 85c19d63f9fb..000000000000 --- a/trunk/drivers/dma/virt-dma.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Virtual DMA channel support for DMAengine - * - * Copyright (C) 2012 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef VIRT_DMA_H -#define VIRT_DMA_H - -#include -#include - -#include "dmaengine.h" - -struct virt_dma_desc { - struct dma_async_tx_descriptor tx; - /* protected by vc.lock */ - struct list_head node; -}; - -struct virt_dma_chan { - struct dma_chan chan; - struct tasklet_struct task; - void (*desc_free)(struct virt_dma_desc *); - - spinlock_t lock; - - /* protected by vc.lock */ - struct list_head desc_submitted; - struct list_head desc_issued; - struct list_head desc_completed; - - struct virt_dma_desc *cyclic; -}; - -static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan) -{ - return container_of(chan, struct virt_dma_chan, chan); -} - -void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head); -void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev); -struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *, dma_cookie_t); - -/** - * vchan_tx_prep - prepare a descriptor - * vc: virtual channel allocating this descriptor - * vd: virtual descriptor to prepare - * tx_flags: flags argument passed in to prepare function - */ -static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan *vc, - struct virt_dma_desc *vd, unsigned long tx_flags) -{ - extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *); - - dma_async_tx_descriptor_init(&vd->tx, &vc->chan); - vd->tx.flags = tx_flags; - vd->tx.tx_submit = vchan_tx_submit; - - return &vd->tx; -} - -/** - * vchan_issue_pending - move submitted descriptors to issued list - * vc: virtual channel to update - * - * vc.lock must be held by caller - */ -static inline bool vchan_issue_pending(struct virt_dma_chan *vc) -{ - list_splice_tail_init(&vc->desc_submitted, &vc->desc_issued); - return !list_empty(&vc->desc_issued); -} - -/** - * vchan_cookie_complete - report completion of a descriptor - * vd: virtual descriptor to update - * - * vc.lock must be held by caller - */ -static inline void vchan_cookie_complete(struct virt_dma_desc *vd) -{ - struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); - - dma_cookie_complete(&vd->tx); - dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n", - vd, vd->tx.cookie); - list_add_tail(&vd->node, &vc->desc_completed); - - tasklet_schedule(&vc->task); -} - -/** - * vchan_cyclic_callback - report the completion of a period - * vd: virtual descriptor - */ -static inline void vchan_cyclic_callback(struct virt_dma_desc *vd) -{ - struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); - - vc->cyclic = vd; - tasklet_schedule(&vc->task); -} - -/** - * vchan_next_desc - peek at the next descriptor to be processed - * vc: virtual channel to obtain descriptor from - * - * vc.lock must be held by caller - */ -static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc) -{ - if (list_empty(&vc->desc_issued)) - return NULL; - - return list_first_entry(&vc->desc_issued, struct virt_dma_desc, node); -} - -/** - * vchan_get_all_descriptors - obtain all submitted and issued descriptors - * vc: virtual channel to get descriptors from - * head: list of descriptors found - * - * vc.lock must be held by caller - * - * Removes all submitted and issued descriptors from internal lists, and - * provides a list of all descriptors found - */ -static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc, - struct list_head *head) -{ - list_splice_tail_init(&vc->desc_submitted, head); - list_splice_tail_init(&vc->desc_issued, head); - list_splice_tail_init(&vc->desc_completed, head); -} - -static inline void vchan_free_chan_resources(struct virt_dma_chan *vc) -{ - unsigned long flags; - LIST_HEAD(head); - - spin_lock_irqsave(&vc->lock, flags); - vchan_get_all_descriptors(vc, &head); - spin_unlock_irqrestore(&vc->lock, flags); - - vchan_dma_desc_free_list(vc, &head); -} - -#endif diff --git a/trunk/drivers/firmware/dmi_scan.c b/trunk/drivers/firmware/dmi_scan.c index b298158cb922..153980be4ee6 100644 --- a/trunk/drivers/firmware/dmi_scan.c +++ b/trunk/drivers/firmware/dmi_scan.c @@ -6,7 +6,6 @@ #include #include #include -#include #include /* @@ -112,8 +111,6 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, dmi_table(buf, dmi_len, dmi_num, decode, NULL); - add_device_randomness(buf, dmi_len); - dmi_iounmap(buf, dmi_len); return 0; } diff --git a/trunk/drivers/hid/hid-core.c b/trunk/drivers/hid/hid-core.c index 60ea284407ce..500844f04f93 100644 --- a/trunk/drivers/hid/hid-core.c +++ b/trunk/drivers/hid/hid-core.c @@ -1928,7 +1928,6 @@ 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) }, diff --git a/trunk/drivers/hid/hid-ids.h b/trunk/drivers/hid/hid-ids.h index 1dcb76ff51e3..41c34f21bd00 100644 --- a/trunk/drivers/hid/hid-ids.h +++ b/trunk/drivers/hid/hid-ids.h @@ -333,7 +333,6 @@ #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 diff --git a/trunk/drivers/hv/vmbus_drv.c b/trunk/drivers/hv/vmbus_drv.c index 4748086eaaf2..a220e5746d67 100644 --- a/trunk/drivers/hv/vmbus_drv.c +++ b/trunk/drivers/hv/vmbus_drv.c @@ -545,7 +545,8 @@ static int vmbus_bus_init(int irq) if (ret) goto err_cleanup; - ret = request_irq(irq, vmbus_isr, 0, driver_name, hv_acpi_dev); + ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM, + driver_name, hv_acpi_dev); if (ret != 0) { pr_err("Unable to request IRQ %d\n", diff --git a/trunk/drivers/i2c/busses/i2c-pmcmsp.c b/trunk/drivers/i2c/busses/i2c-pmcmsp.c index 3d71395ae1f7..07b7447ecbc9 100644 --- a/trunk/drivers/i2c/busses/i2c-pmcmsp.c +++ b/trunk/drivers/i2c/busses/i2c-pmcmsp.c @@ -306,7 +306,8 @@ static int __devinit pmcmsptwi_probe(struct platform_device *pldev) pmcmsptwi_data.irq = platform_get_irq(pldev, 0); if (pmcmsptwi_data.irq) { rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt, - IRQF_SHARED, pldev->name, &pmcmsptwi_data); + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + pldev->name, &pmcmsptwi_data); if (rc == 0) { /* * Enable 'DONE' interrupt only. diff --git a/trunk/drivers/infiniband/core/cma.c b/trunk/drivers/infiniband/core/cma.c index 7172559ce0c1..5a335b5447c6 100644 --- a/trunk/drivers/infiniband/core/cma.c +++ b/trunk/drivers/infiniband/core/cma.c @@ -3064,7 +3064,10 @@ static int cma_join_ib_multicast(struct rdma_id_private *id_priv, id_priv->id.port_num, &rec, comp_mask, GFP_KERNEL, cma_ib_mc_handler, mc); - return PTR_RET(mc->multicast.ib); + if (IS_ERR(mc->multicast.ib)) + return PTR_ERR(mc->multicast.ib); + + return 0; } static void iboe_mcast_work_handler(struct work_struct *work) diff --git a/trunk/drivers/infiniband/core/ucma.c b/trunk/drivers/infiniband/core/ucma.c index 6bf850422895..893cb879462c 100644 --- a/trunk/drivers/infiniband/core/ucma.c +++ b/trunk/drivers/infiniband/core/ucma.c @@ -1002,18 +1002,23 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf, if (IS_ERR(ctx)) return PTR_ERR(ctx); - optval = memdup_user((void __user *) (unsigned long) cmd.optval, - cmd.optlen); - if (IS_ERR(optval)) { - ret = PTR_ERR(optval); - goto out; + optval = kmalloc(cmd.optlen, GFP_KERNEL); + if (!optval) { + ret = -ENOMEM; + goto out1; + } + + if (copy_from_user(optval, (void __user *) (unsigned long) cmd.optval, + cmd.optlen)) { + ret = -EFAULT; + goto out2; } ret = ucma_set_option_level(ctx, cmd.level, cmd.optname, optval, cmd.optlen); +out2: kfree(optval); - -out: +out1: ucma_put_ctx(ctx); return ret; } diff --git a/trunk/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/trunk/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index cb5b7f7d4d38..b2f9784beb4a 100644 --- a/trunk/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/trunk/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -893,9 +893,7 @@ static int ocrdma_check_qp_params(struct ib_pd *ibpd, struct ocrdma_dev *dev, /* verify consumer QPs are not trying to use GSI QP's CQ */ if ((attrs->qp_type != IB_QPT_GSI) && (dev->gsi_qp_created)) { if ((dev->gsi_sqcq == get_ocrdma_cq(attrs->send_cq)) || - (dev->gsi_sqcq == get_ocrdma_cq(attrs->recv_cq)) || - (dev->gsi_rqcq == get_ocrdma_cq(attrs->send_cq)) || - (dev->gsi_rqcq == get_ocrdma_cq(attrs->recv_cq))) { + (dev->gsi_sqcq == get_ocrdma_cq(attrs->send_cq))) { ocrdma_err("%s(%d) Consumer QP cannot use GSI CQs.\n", __func__, dev->id); return -EINVAL; diff --git a/trunk/drivers/infiniband/hw/qib/qib.h b/trunk/drivers/infiniband/hw/qib/qib.h index 7b1b86690024..6e19ec844d99 100644 --- a/trunk/drivers/infiniband/hw/qib/qib.h +++ b/trunk/drivers/infiniband/hw/qib/qib.h @@ -656,11 +656,6 @@ struct qib_pportdata { /* 16 congestion entries with each entry corresponding to a SL */ struct ib_cc_congestion_entry_shadow *congestion_entries; - /* Maximum number of congestion control entries that the agent expects - * the manager to send. - */ - u16 cc_supported_table_entries; - /* Total number of congestion control table entries */ u16 total_cct_entry; @@ -672,6 +667,11 @@ struct qib_pportdata { /* CA's max number of 64 entry units in the congestion control table */ u8 cc_max_table_entries; + + /* Maximum number of congestion control entries that the agent expects + * the manager to send. + */ + u8 cc_supported_table_entries; }; /* Observers. Not to be taken lightly, possibly not to ship. */ diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib.h b/trunk/drivers/infiniband/ulp/ipoib/ipoib.h index ca43901ed861..86df632ea612 100644 --- a/trunk/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib.h @@ -92,8 +92,6 @@ enum { IPOIB_STOP_REAPER = 7, IPOIB_FLAG_ADMIN_CM = 9, IPOIB_FLAG_UMCAST = 10, - IPOIB_STOP_NEIGH_GC = 11, - IPOIB_NEIGH_TBL_FLUSH = 12, IPOIB_MAX_BACKOFF_SECONDS = 16, @@ -262,20 +260,6 @@ struct ipoib_ethtool_st { u16 max_coalesced_frames; }; -struct ipoib_neigh_hash { - struct ipoib_neigh __rcu **buckets; - struct rcu_head rcu; - u32 mask; - u32 size; -}; - -struct ipoib_neigh_table { - struct ipoib_neigh_hash __rcu *htbl; - rwlock_t rwlock; - atomic_t entries; - struct completion flushed; -}; - /* * Device private locking: network stack tx_lock protects members used * in TX fast path, lock protects everything else. lock nests inside @@ -295,8 +279,6 @@ struct ipoib_dev_priv { struct rb_root path_tree; struct list_head path_list; - struct ipoib_neigh_table ntbl; - struct ipoib_mcast *broadcast; struct list_head multicast_list; struct rb_root multicast_tree; @@ -309,7 +291,7 @@ struct ipoib_dev_priv { struct work_struct flush_heavy; struct work_struct restart_task; struct delayed_work ah_reap_task; - struct delayed_work neigh_reap_task; + struct ib_device *ca; u8 port; u16 pkey; @@ -395,16 +377,13 @@ struct ipoib_neigh { #ifdef CONFIG_INFINIBAND_IPOIB_CM struct ipoib_cm_tx *cm; #endif - u8 daddr[INFINIBAND_ALEN]; + union ib_gid dgid; struct sk_buff_head queue; + struct neighbour *neighbour; struct net_device *dev; struct list_head list; - struct ipoib_neigh __rcu *hnext; - struct rcu_head rcu; - atomic_t refcnt; - unsigned long alive; }; #define IPOIB_UD_MTU(ib_mtu) (ib_mtu - IPOIB_ENCAP_LEN) @@ -415,17 +394,21 @@ static inline int ipoib_ud_need_sg(unsigned int ib_mtu) return IPOIB_UD_BUF_SIZE(ib_mtu) > PAGE_SIZE; } -void ipoib_neigh_dtor(struct ipoib_neigh *neigh); -static inline void ipoib_neigh_put(struct ipoib_neigh *neigh) +/* + * We stash a pointer to our private neighbour information after our + * hardware address in neigh->ha. The ALIGN() expression here makes + * sure that this pointer is stored aligned so that an unaligned + * load is not needed to dereference it. + */ +static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh) { - if (atomic_dec_and_test(&neigh->refcnt)) - ipoib_neigh_dtor(neigh); + return (void*) neigh + ALIGN(offsetof(struct neighbour, ha) + + INFINIBAND_ALEN, sizeof(void *)); } -struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr); -struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr, + +struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh, struct net_device *dev); -void ipoib_neigh_free(struct ipoib_neigh *neigh); -void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid); +void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh); extern struct workqueue_struct *ipoib_workqueue; @@ -442,6 +425,7 @@ static inline void ipoib_put_ah(struct ipoib_ah *ah) { kref_put(&ah->ref, ipoib_free_ah); } + int ipoib_open(struct net_device *dev); int ipoib_add_pkey_attr(struct net_device *dev); int ipoib_add_umcast_attr(struct net_device *dev); @@ -471,7 +455,7 @@ void ipoib_dev_cleanup(struct net_device *dev); void ipoib_mcast_join_task(struct work_struct *work); void ipoib_mcast_carrier_on_task(struct work_struct *work); -void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb); +void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb); void ipoib_mcast_restart_task(struct work_struct *work); int ipoib_mcast_start_thread(struct net_device *dev); @@ -533,10 +517,10 @@ static inline int ipoib_cm_admin_enabled(struct net_device *dev) test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags); } -static inline int ipoib_cm_enabled(struct net_device *dev, u8 *hwaddr) +static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n) { struct ipoib_dev_priv *priv = netdev_priv(dev); - return IPOIB_CM_SUPPORTED(hwaddr) && + return IPOIB_CM_SUPPORTED(n->ha) && test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags); } @@ -591,7 +575,7 @@ static inline int ipoib_cm_admin_enabled(struct net_device *dev) { return 0; } -static inline int ipoib_cm_enabled(struct net_device *dev, u8 *hwaddr) +static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n) { return 0; diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 95ecf4eadf5f..6d66ab0dd92a 100644 --- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -811,7 +811,9 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc) if (neigh) { neigh->cm = NULL; list_del(&neigh->list); - ipoib_neigh_free(neigh); + if (neigh->ah) + ipoib_put_ah(neigh->ah); + ipoib_neigh_free(dev, neigh); tx->neigh = NULL; } @@ -1228,7 +1230,9 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id, if (neigh) { neigh->cm = NULL; list_del(&neigh->list); - ipoib_neigh_free(neigh); + if (neigh->ah) + ipoib_put_ah(neigh->ah); + ipoib_neigh_free(dev, neigh); tx->neigh = NULL; } @@ -1275,7 +1279,7 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx) list_move(&tx->list, &priv->cm.reap_list); queue_work(ipoib_workqueue, &priv->cm.reap_task); ipoib_dbg(priv, "Reap connection for gid %pI6\n", - tx->neigh->daddr + 4); + tx->neigh->dgid.raw); tx->neigh = NULL; } } @@ -1300,7 +1304,7 @@ static void ipoib_cm_tx_start(struct work_struct *work) p = list_entry(priv->cm.start_list.next, typeof(*p), list); list_del_init(&p->list); neigh = p->neigh; - qpn = IPOIB_QPN(neigh->daddr); + qpn = IPOIB_QPN(neigh->neighbour->ha); memcpy(&pathrec, &p->path->pathrec, sizeof pathrec); spin_unlock_irqrestore(&priv->lock, flags); @@ -1316,7 +1320,9 @@ static void ipoib_cm_tx_start(struct work_struct *work) if (neigh) { neigh->cm = NULL; list_del(&neigh->list); - ipoib_neigh_free(neigh); + if (neigh->ah) + ipoib_put_ah(neigh->ah); + ipoib_neigh_free(dev, neigh); } list_del(&p->list); kfree(p); diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c index 97920b77a5d0..bbee4b2d7a13 100644 --- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -46,8 +46,7 @@ #include #include -#include -#include +#include MODULE_AUTHOR("Roland Dreier"); MODULE_DESCRIPTION("IP-over-InfiniBand net driver"); @@ -85,7 +84,6 @@ struct ib_sa_client ipoib_sa_client; static void ipoib_add_one(struct ib_device *device); static void ipoib_remove_one(struct ib_device *device); -static void ipoib_neigh_reclaim(struct rcu_head *rp); static struct ib_client ipoib_client = { .name = "ipoib", @@ -266,15 +264,30 @@ static int __path_add(struct net_device *dev, struct ipoib_path *path) static void path_free(struct net_device *dev, struct ipoib_path *path) { + struct ipoib_dev_priv *priv = netdev_priv(dev); + struct ipoib_neigh *neigh, *tn; struct sk_buff *skb; + unsigned long flags; while ((skb = __skb_dequeue(&path->queue))) dev_kfree_skb_irq(skb); - ipoib_dbg(netdev_priv(dev), "path_free\n"); + spin_lock_irqsave(&priv->lock, flags); + + list_for_each_entry_safe(neigh, tn, &path->neigh_list, list) { + /* + * It's safe to call ipoib_put_ah() inside priv->lock + * here, because we know that path->ah will always + * hold one more reference, so ipoib_put_ah() will + * never do more than decrement the ref count. + */ + if (neigh->ah) + ipoib_put_ah(neigh->ah); + + ipoib_neigh_free(dev, neigh); + } - /* remove all neigh connected to this path */ - ipoib_del_neighs_by_gid(dev, path->pathrec.dgid.raw); + spin_unlock_irqrestore(&priv->lock, flags); if (path->ah) ipoib_put_ah(path->ah); @@ -445,15 +458,19 @@ static void path_rec_completion(int status, } kref_get(&path->ah->ref); neigh->ah = path->ah; + memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw, + sizeof(union ib_gid)); - if (ipoib_cm_enabled(dev, neigh->daddr)) { + if (ipoib_cm_enabled(dev, neigh->neighbour)) { if (!ipoib_cm_get(neigh)) ipoib_cm_set(neigh, ipoib_cm_create_tx(dev, path, neigh)); if (!ipoib_cm_get(neigh)) { list_del(&neigh->list); - ipoib_neigh_free(neigh); + if (neigh->ah) + ipoib_put_ah(neigh->ah); + ipoib_neigh_free(dev, neigh); continue; } } @@ -538,15 +555,15 @@ static int path_rec_start(struct net_device *dev, return 0; } -static void neigh_add_path(struct sk_buff *skb, u8 *daddr, - struct net_device *dev) +/* called with rcu_read_lock */ +static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_path *path; struct ipoib_neigh *neigh; unsigned long flags; - neigh = ipoib_neigh_alloc(daddr, dev); + neigh = ipoib_neigh_alloc(n, skb->dev); if (!neigh) { ++dev->stats.tx_dropped; dev_kfree_skb_any(skb); @@ -555,9 +572,9 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, spin_lock_irqsave(&priv->lock, flags); - path = __path_find(dev, daddr + 4); + path = __path_find(dev, n->ha + 4); if (!path) { - path = path_rec_create(dev, daddr + 4); + path = path_rec_create(dev, n->ha + 4); if (!path) goto err_path; @@ -569,13 +586,17 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, if (path->ah) { kref_get(&path->ah->ref); neigh->ah = path->ah; + memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw, + sizeof(union ib_gid)); - if (ipoib_cm_enabled(dev, neigh->daddr)) { + if (ipoib_cm_enabled(dev, neigh->neighbour)) { if (!ipoib_cm_get(neigh)) ipoib_cm_set(neigh, ipoib_cm_create_tx(dev, path, neigh)); if (!ipoib_cm_get(neigh)) { list_del(&neigh->list); - ipoib_neigh_free(neigh); + if (neigh->ah) + ipoib_put_ah(neigh->ah); + ipoib_neigh_free(dev, neigh); goto err_drop; } if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) @@ -587,8 +608,7 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, } } else { spin_unlock_irqrestore(&priv->lock, flags); - ipoib_send(dev, skb, path->ah, IPOIB_QPN(daddr)); - ipoib_neigh_put(neigh); + ipoib_send(dev, skb, path->ah, IPOIB_QPN(n->ha)); return; } } else { @@ -601,20 +621,35 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, } spin_unlock_irqrestore(&priv->lock, flags); - ipoib_neigh_put(neigh); return; err_list: list_del(&neigh->list); err_path: - ipoib_neigh_free(neigh); + ipoib_neigh_free(dev, neigh); err_drop: ++dev->stats.tx_dropped; dev_kfree_skb_any(skb); spin_unlock_irqrestore(&priv->lock, flags); - ipoib_neigh_put(neigh); +} + +/* called with rcu_read_lock */ +static void ipoib_path_lookup(struct sk_buff *skb, struct neighbour *n, struct net_device *dev) +{ + struct ipoib_dev_priv *priv = netdev_priv(skb->dev); + + /* Look up path record for unicasts */ + if (n->ha[4] != 0xff) { + neigh_add_path(skb, n, dev); + return; + } + + /* Add in the P_Key for multicasts */ + n->ha[8] = (priv->pkey >> 8) & 0xff; + n->ha[9] = priv->pkey & 0xff; + ipoib_mcast_send(dev, n->ha + 4, skb); } static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, @@ -675,80 +710,96 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_neigh *neigh; - struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; - struct ipoib_header *header; + struct neighbour *n = NULL; unsigned long flags; - header = (struct ipoib_header *) skb->data; - - if (unlikely(cb->hwaddr[4] == 0xff)) { - /* multicast, arrange "if" according to probability */ - if ((header->proto != htons(ETH_P_IP)) && - (header->proto != htons(ETH_P_IPV6)) && - (header->proto != htons(ETH_P_ARP)) && - (header->proto != htons(ETH_P_RARP))) { - /* ethertype not supported by IPoIB */ + rcu_read_lock(); + if (likely(skb_dst(skb))) { + n = dst_neigh_lookup_skb(skb_dst(skb), skb); + if (!n) { ++dev->stats.tx_dropped; dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + goto unlock; } - /* Add in the P_Key for multicast*/ - cb->hwaddr[8] = (priv->pkey >> 8) & 0xff; - cb->hwaddr[9] = priv->pkey & 0xff; - - neigh = ipoib_neigh_get(dev, cb->hwaddr); - if (likely(neigh)) - goto send_using_neigh; - ipoib_mcast_send(dev, cb->hwaddr, skb); - return NETDEV_TX_OK; } + if (likely(n)) { + if (unlikely(!*to_ipoib_neigh(n))) { + ipoib_path_lookup(skb, n, dev); + goto unlock; + } + + neigh = *to_ipoib_neigh(n); - /* unicast, arrange "switch" according to probability */ - switch (header->proto) { - case htons(ETH_P_IP): - case htons(ETH_P_IPV6): - neigh = ipoib_neigh_get(dev, cb->hwaddr); - if (unlikely(!neigh)) { - neigh_add_path(skb, cb->hwaddr, dev); - return NETDEV_TX_OK; + if (unlikely((memcmp(&neigh->dgid.raw, + n->ha + 4, + sizeof(union ib_gid))) || + (neigh->dev != dev))) { + spin_lock_irqsave(&priv->lock, flags); + /* + * It's safe to call ipoib_put_ah() inside + * priv->lock here, because we know that + * path->ah will always hold one more reference, + * so ipoib_put_ah() will never do more than + * decrement the ref count. + */ + if (neigh->ah) + ipoib_put_ah(neigh->ah); + list_del(&neigh->list); + ipoib_neigh_free(dev, neigh); + spin_unlock_irqrestore(&priv->lock, flags); + ipoib_path_lookup(skb, n, dev); + goto unlock; } - break; - case htons(ETH_P_ARP): - case htons(ETH_P_RARP): - /* for unicast ARP and RARP should always perform path find */ - unicast_arp_send(skb, dev, cb); - return NETDEV_TX_OK; - default: - /* ethertype not supported by IPoIB */ - ++dev->stats.tx_dropped; - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } -send_using_neigh: - /* note we now hold a ref to neigh */ - if (ipoib_cm_get(neigh)) { - if (ipoib_cm_up(neigh)) { - ipoib_cm_send(dev, skb, ipoib_cm_get(neigh)); - goto unref; + if (ipoib_cm_get(neigh)) { + if (ipoib_cm_up(neigh)) { + ipoib_cm_send(dev, skb, ipoib_cm_get(neigh)); + goto unlock; + } + } else if (neigh->ah) { + ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha)); + goto unlock; } - } else if (neigh->ah) { - ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr)); - goto unref; - } - if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { - spin_lock_irqsave(&priv->lock, flags); - __skb_queue_tail(&neigh->queue, skb); - spin_unlock_irqrestore(&priv->lock, flags); + if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + spin_lock_irqsave(&priv->lock, flags); + __skb_queue_tail(&neigh->queue, skb); + spin_unlock_irqrestore(&priv->lock, flags); + } else { + ++dev->stats.tx_dropped; + dev_kfree_skb_any(skb); + } } else { - ++dev->stats.tx_dropped; - dev_kfree_skb_any(skb); - } + struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; -unref: - ipoib_neigh_put(neigh); + if (cb->hwaddr[4] == 0xff) { + /* Add in the P_Key for multicast*/ + cb->hwaddr[8] = (priv->pkey >> 8) & 0xff; + cb->hwaddr[9] = priv->pkey & 0xff; + ipoib_mcast_send(dev, cb->hwaddr + 4, skb); + } else { + /* unicast GID -- should be ARP or RARP reply */ + + if ((be16_to_cpup((__be16 *) skb->data) != ETH_P_ARP) && + (be16_to_cpup((__be16 *) skb->data) != ETH_P_RARP)) { + ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x %pI6\n", + skb_dst(skb) ? "neigh" : "dst", + be16_to_cpup((__be16 *) skb->data), + IPOIB_QPN(cb->hwaddr), + cb->hwaddr + 4); + dev_kfree_skb_any(skb); + ++dev->stats.tx_dropped; + goto unlock; + } + + unicast_arp_send(skb, dev, cb); + } + } +unlock: + if (n) + neigh_release(n); + rcu_read_unlock(); return NETDEV_TX_OK; } @@ -770,7 +821,6 @@ static int ipoib_hard_header(struct sk_buff *skb, const void *daddr, const void *saddr, unsigned len) { struct ipoib_header *header; - struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; header = (struct ipoib_header *) skb_push(skb, sizeof *header); @@ -778,11 +828,14 @@ static int ipoib_hard_header(struct sk_buff *skb, header->reserved = 0; /* - * we don't rely on dst_entry structure, always stuff the + * If we don't have a dst_entry structure, stuff the * destination address into skb->cb so we can figure out where * to send the packet later. */ - memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN); + if (!skb_dst(skb)) { + struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; + memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN); + } return 0; } @@ -799,438 +852,86 @@ static void ipoib_set_mcast_list(struct net_device *dev) queue_work(ipoib_workqueue, &priv->restart_task); } -static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr) -{ - /* - * Use only the address parts that contributes to spreading - * The subnet prefix is not used as one can not connect to - * same remote port (GUID) using the same remote QPN via two - * different subnets. - */ - /* qpn octets[1:4) & port GUID octets[12:20) */ - u32 *daddr_32 = (u32 *) daddr; - u32 hv; - - hv = jhash_3words(daddr_32[3], daddr_32[4], 0xFFFFFF & daddr_32[0], 0); - return hv & htbl->mask; -} - -struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr) +static void ipoib_neigh_cleanup(struct neighbour *n) { - struct ipoib_dev_priv *priv = netdev_priv(dev); - struct ipoib_neigh_table *ntbl = &priv->ntbl; - struct ipoib_neigh_hash *htbl; - struct ipoib_neigh *neigh = NULL; - u32 hash_val; - - rcu_read_lock_bh(); - - htbl = rcu_dereference_bh(ntbl->htbl); - - if (!htbl) - goto out_unlock; - - hash_val = ipoib_addr_hash(htbl, daddr); - for (neigh = rcu_dereference_bh(htbl->buckets[hash_val]); - neigh != NULL; - neigh = rcu_dereference_bh(neigh->hnext)) { - if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) { - /* found, take one ref on behalf of the caller */ - if (!atomic_inc_not_zero(&neigh->refcnt)) { - /* deleted */ - neigh = NULL; - goto out_unlock; - } - neigh->alive = jiffies; - goto out_unlock; - } - } - -out_unlock: - rcu_read_unlock_bh(); - return neigh; -} - -static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv) -{ - struct ipoib_neigh_table *ntbl = &priv->ntbl; - struct ipoib_neigh_hash *htbl; - unsigned long neigh_obsolete; - unsigned long dt; + struct ipoib_neigh *neigh; + struct ipoib_dev_priv *priv = netdev_priv(n->dev); unsigned long flags; - int i; + struct ipoib_ah *ah = NULL; - if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags)) + neigh = *to_ipoib_neigh(n); + if (neigh) + priv = netdev_priv(neigh->dev); + else return; + ipoib_dbg(priv, + "neigh_cleanup for %06x %pI6\n", + IPOIB_QPN(n->ha), + n->ha + 4); - write_lock_bh(&ntbl->rwlock); - - htbl = rcu_dereference_protected(ntbl->htbl, - lockdep_is_held(&ntbl->rwlock)); - - if (!htbl) - goto out_unlock; - - /* neigh is obsolete if it was idle for two GC periods */ - dt = 2 * arp_tbl.gc_interval; - neigh_obsolete = jiffies - dt; - /* handle possible race condition */ - if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags)) - goto out_unlock; - - for (i = 0; i < htbl->size; i++) { - struct ipoib_neigh *neigh; - struct ipoib_neigh __rcu **np = &htbl->buckets[i]; - - while ((neigh = rcu_dereference_protected(*np, - lockdep_is_held(&ntbl->rwlock))) != NULL) { - /* was the neigh idle for two GC periods */ - if (time_after(neigh_obsolete, neigh->alive)) { - rcu_assign_pointer(*np, - rcu_dereference_protected(neigh->hnext, - lockdep_is_held(&ntbl->rwlock))); - /* remove from path/mc list */ - spin_lock_irqsave(&priv->lock, flags); - list_del(&neigh->list); - spin_unlock_irqrestore(&priv->lock, flags); - call_rcu(&neigh->rcu, ipoib_neigh_reclaim); - } else { - np = &neigh->hnext; - } - - } - } - -out_unlock: - write_unlock_bh(&ntbl->rwlock); -} + spin_lock_irqsave(&priv->lock, flags); -static void ipoib_reap_neigh(struct work_struct *work) -{ - struct ipoib_dev_priv *priv = - container_of(work, struct ipoib_dev_priv, neigh_reap_task.work); + if (neigh->ah) + ah = neigh->ah; + list_del(&neigh->list); + ipoib_neigh_free(n->dev, neigh); - __ipoib_reap_neigh(priv); + spin_unlock_irqrestore(&priv->lock, flags); - if (!test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags)) - queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task, - arp_tbl.gc_interval); + if (ah) + ipoib_put_ah(ah); } - -static struct ipoib_neigh *ipoib_neigh_ctor(u8 *daddr, +struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour, struct net_device *dev) { struct ipoib_neigh *neigh; - neigh = kzalloc(sizeof *neigh, GFP_ATOMIC); + neigh = kmalloc(sizeof *neigh, GFP_ATOMIC); if (!neigh) return NULL; + neigh->neighbour = neighbour; neigh->dev = dev; - memcpy(&neigh->daddr, daddr, sizeof(neigh->daddr)); + memset(&neigh->dgid.raw, 0, sizeof (union ib_gid)); + *to_ipoib_neigh(neighbour) = neigh; skb_queue_head_init(&neigh->queue); - INIT_LIST_HEAD(&neigh->list); ipoib_cm_set(neigh, NULL); - /* one ref on behalf of the caller */ - atomic_set(&neigh->refcnt, 1); - - return neigh; -} - -struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr, - struct net_device *dev) -{ - struct ipoib_dev_priv *priv = netdev_priv(dev); - struct ipoib_neigh_table *ntbl = &priv->ntbl; - struct ipoib_neigh_hash *htbl; - struct ipoib_neigh *neigh; - u32 hash_val; - - write_lock_bh(&ntbl->rwlock); - - htbl = rcu_dereference_protected(ntbl->htbl, - lockdep_is_held(&ntbl->rwlock)); - if (!htbl) { - neigh = NULL; - goto out_unlock; - } - - /* need to add a new neigh, but maybe some other thread succeeded? - * recalc hash, maybe hash resize took place so we do a search - */ - hash_val = ipoib_addr_hash(htbl, daddr); - for (neigh = rcu_dereference_protected(htbl->buckets[hash_val], - lockdep_is_held(&ntbl->rwlock)); - neigh != NULL; - neigh = rcu_dereference_protected(neigh->hnext, - lockdep_is_held(&ntbl->rwlock))) { - if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) { - /* found, take one ref on behalf of the caller */ - if (!atomic_inc_not_zero(&neigh->refcnt)) { - /* deleted */ - neigh = NULL; - break; - } - neigh->alive = jiffies; - goto out_unlock; - } - } - - neigh = ipoib_neigh_ctor(daddr, dev); - if (!neigh) - goto out_unlock; - - /* one ref on behalf of the hash table */ - atomic_inc(&neigh->refcnt); - neigh->alive = jiffies; - /* put in hash */ - rcu_assign_pointer(neigh->hnext, - rcu_dereference_protected(htbl->buckets[hash_val], - lockdep_is_held(&ntbl->rwlock))); - rcu_assign_pointer(htbl->buckets[hash_val], neigh); - atomic_inc(&ntbl->entries); - -out_unlock: - write_unlock_bh(&ntbl->rwlock); return neigh; } -void ipoib_neigh_dtor(struct ipoib_neigh *neigh) +void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh) { - /* neigh reference count was dropprd to zero */ - struct net_device *dev = neigh->dev; - struct ipoib_dev_priv *priv = netdev_priv(dev); struct sk_buff *skb; - if (neigh->ah) - ipoib_put_ah(neigh->ah); + *to_ipoib_neigh(neigh->neighbour) = NULL; while ((skb = __skb_dequeue(&neigh->queue))) { ++dev->stats.tx_dropped; dev_kfree_skb_any(skb); } if (ipoib_cm_get(neigh)) ipoib_cm_destroy_tx(ipoib_cm_get(neigh)); - ipoib_dbg(netdev_priv(dev), - "neigh free for %06x %pI6\n", - IPOIB_QPN(neigh->daddr), - neigh->daddr + 4); kfree(neigh); - if (atomic_dec_and_test(&priv->ntbl.entries)) { - if (test_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags)) - complete(&priv->ntbl.flushed); - } -} - -static void ipoib_neigh_reclaim(struct rcu_head *rp) -{ - /* Called as a result of removal from hash table */ - struct ipoib_neigh *neigh = container_of(rp, struct ipoib_neigh, rcu); - /* note TX context may hold another ref */ - ipoib_neigh_put(neigh); } -void ipoib_neigh_free(struct ipoib_neigh *neigh) +static int ipoib_neigh_setup_dev(struct net_device *dev, struct neigh_parms *parms) { - struct net_device *dev = neigh->dev; - struct ipoib_dev_priv *priv = netdev_priv(dev); - struct ipoib_neigh_table *ntbl = &priv->ntbl; - struct ipoib_neigh_hash *htbl; - struct ipoib_neigh __rcu **np; - struct ipoib_neigh *n; - u32 hash_val; - - write_lock_bh(&ntbl->rwlock); - - htbl = rcu_dereference_protected(ntbl->htbl, - lockdep_is_held(&ntbl->rwlock)); - if (!htbl) - goto out_unlock; - - hash_val = ipoib_addr_hash(htbl, neigh->daddr); - np = &htbl->buckets[hash_val]; - for (n = rcu_dereference_protected(*np, - lockdep_is_held(&ntbl->rwlock)); - n != NULL; - n = rcu_dereference_protected(neigh->hnext, - lockdep_is_held(&ntbl->rwlock))) { - if (n == neigh) { - /* found */ - rcu_assign_pointer(*np, - rcu_dereference_protected(neigh->hnext, - lockdep_is_held(&ntbl->rwlock))); - call_rcu(&neigh->rcu, ipoib_neigh_reclaim); - goto out_unlock; - } else { - np = &n->hnext; - } - } - -out_unlock: - write_unlock_bh(&ntbl->rwlock); - -} - -static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv) -{ - struct ipoib_neigh_table *ntbl = &priv->ntbl; - struct ipoib_neigh_hash *htbl; - struct ipoib_neigh **buckets; - u32 size; - - clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags); - ntbl->htbl = NULL; - rwlock_init(&ntbl->rwlock); - htbl = kzalloc(sizeof(*htbl), GFP_KERNEL); - if (!htbl) - return -ENOMEM; - set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); - size = roundup_pow_of_two(arp_tbl.gc_thresh3); - buckets = kzalloc(size * sizeof(*buckets), GFP_KERNEL); - if (!buckets) { - kfree(htbl); - return -ENOMEM; - } - htbl->size = size; - htbl->mask = (size - 1); - htbl->buckets = buckets; - ntbl->htbl = htbl; - atomic_set(&ntbl->entries, 0); - - /* start garbage collection */ - clear_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); - queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task, - arp_tbl.gc_interval); + parms->neigh_cleanup = ipoib_neigh_cleanup; return 0; } -static void neigh_hash_free_rcu(struct rcu_head *head) -{ - struct ipoib_neigh_hash *htbl = container_of(head, - struct ipoib_neigh_hash, - rcu); - struct ipoib_neigh __rcu **buckets = htbl->buckets; - - kfree(buckets); - kfree(htbl); -} - -void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid) -{ - struct ipoib_dev_priv *priv = netdev_priv(dev); - struct ipoib_neigh_table *ntbl = &priv->ntbl; - struct ipoib_neigh_hash *htbl; - unsigned long flags; - int i; - - /* remove all neigh connected to a given path or mcast */ - write_lock_bh(&ntbl->rwlock); - - htbl = rcu_dereference_protected(ntbl->htbl, - lockdep_is_held(&ntbl->rwlock)); - - if (!htbl) - goto out_unlock; - - for (i = 0; i < htbl->size; i++) { - struct ipoib_neigh *neigh; - struct ipoib_neigh __rcu **np = &htbl->buckets[i]; - - while ((neigh = rcu_dereference_protected(*np, - lockdep_is_held(&ntbl->rwlock))) != NULL) { - /* delete neighs belong to this parent */ - if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) { - rcu_assign_pointer(*np, - rcu_dereference_protected(neigh->hnext, - lockdep_is_held(&ntbl->rwlock))); - /* remove from parent list */ - spin_lock_irqsave(&priv->lock, flags); - list_del(&neigh->list); - spin_unlock_irqrestore(&priv->lock, flags); - call_rcu(&neigh->rcu, ipoib_neigh_reclaim); - } else { - np = &neigh->hnext; - } - - } - } -out_unlock: - write_unlock_bh(&ntbl->rwlock); -} - -static void ipoib_flush_neighs(struct ipoib_dev_priv *priv) -{ - struct ipoib_neigh_table *ntbl = &priv->ntbl; - struct ipoib_neigh_hash *htbl; - unsigned long flags; - int i; - - write_lock_bh(&ntbl->rwlock); - - htbl = rcu_dereference_protected(ntbl->htbl, - lockdep_is_held(&ntbl->rwlock)); - if (!htbl) - goto out_unlock; - - for (i = 0; i < htbl->size; i++) { - struct ipoib_neigh *neigh; - struct ipoib_neigh __rcu **np = &htbl->buckets[i]; - - while ((neigh = rcu_dereference_protected(*np, - lockdep_is_held(&ntbl->rwlock))) != NULL) { - rcu_assign_pointer(*np, - rcu_dereference_protected(neigh->hnext, - lockdep_is_held(&ntbl->rwlock))); - /* remove from path/mc list */ - spin_lock_irqsave(&priv->lock, flags); - list_del(&neigh->list); - spin_unlock_irqrestore(&priv->lock, flags); - call_rcu(&neigh->rcu, ipoib_neigh_reclaim); - } - } - - rcu_assign_pointer(ntbl->htbl, NULL); - call_rcu(&htbl->rcu, neigh_hash_free_rcu); - -out_unlock: - write_unlock_bh(&ntbl->rwlock); -} - -static void ipoib_neigh_hash_uninit(struct net_device *dev) -{ - struct ipoib_dev_priv *priv = netdev_priv(dev); - int stopped; - - ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n"); - init_completion(&priv->ntbl.flushed); - set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags); - - /* Stop GC if called at init fail need to cancel work */ - stopped = test_and_set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); - if (!stopped) - cancel_delayed_work(&priv->neigh_reap_task); - - if (atomic_read(&priv->ntbl.entries)) { - ipoib_flush_neighs(priv); - wait_for_completion(&priv->ntbl.flushed); - } -} - - int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port) { struct ipoib_dev_priv *priv = netdev_priv(dev); - if (ipoib_neigh_hash_init(priv) < 0) - goto out; /* Allocate RX/TX "rings" to hold queued skbs */ priv->rx_ring = kzalloc(ipoib_recvq_size * sizeof *priv->rx_ring, GFP_KERNEL); if (!priv->rx_ring) { printk(KERN_WARNING "%s: failed to allocate RX ring (%d entries)\n", ca->name, ipoib_recvq_size); - goto out_neigh_hash_cleanup; + goto out; } priv->tx_ring = vzalloc(ipoib_sendq_size * sizeof *priv->tx_ring); @@ -1253,8 +954,6 @@ int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port) out_rx_ring_cleanup: kfree(priv->rx_ring); -out_neigh_hash_cleanup: - ipoib_neigh_hash_uninit(dev); out: return -ENOMEM; } @@ -1267,9 +966,6 @@ void ipoib_dev_cleanup(struct net_device *dev) /* Delete any child interfaces first */ list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) { - /* Stop GC on child */ - set_bit(IPOIB_STOP_NEIGH_GC, &cpriv->flags); - cancel_delayed_work(&cpriv->neigh_reap_task); unregister_netdev(cpriv->dev); ipoib_dev_cleanup(cpriv->dev); free_netdev(cpriv->dev); @@ -1282,8 +978,6 @@ void ipoib_dev_cleanup(struct net_device *dev) priv->rx_ring = NULL; priv->tx_ring = NULL; - - ipoib_neigh_hash_uninit(dev); } static const struct header_ops ipoib_header_ops = { @@ -1298,6 +992,7 @@ static const struct net_device_ops ipoib_netdev_ops = { .ndo_start_xmit = ipoib_start_xmit, .ndo_tx_timeout = ipoib_timeout, .ndo_set_rx_mode = ipoib_set_mcast_list, + .ndo_neigh_setup = ipoib_neigh_setup_dev, }; static void ipoib_setup(struct net_device *dev) @@ -1346,7 +1041,6 @@ static void ipoib_setup(struct net_device *dev) INIT_WORK(&priv->flush_heavy, ipoib_ib_dev_flush_heavy); INIT_WORK(&priv->restart_task, ipoib_mcast_restart_task); INIT_DELAYED_WORK(&priv->ah_reap_task, ipoib_reap_ah); - INIT_DELAYED_WORK(&priv->neigh_reap_task, ipoib_reap_neigh); } struct ipoib_dev_priv *ipoib_intf_alloc(const char *name) @@ -1587,9 +1281,6 @@ static struct net_device *ipoib_add_port(const char *format, register_failed: ib_unregister_event_handler(&priv->event_handler); - /* Stop GC if started before flush */ - set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); - cancel_delayed_work(&priv->neigh_reap_task); flush_workqueue(ipoib_workqueue); event_failed: @@ -1656,9 +1347,6 @@ static void ipoib_remove_one(struct ib_device *device) dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP); rtnl_unlock(); - /* Stop GC */ - set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); - cancel_delayed_work(&priv->neigh_reap_task); flush_workqueue(ipoib_workqueue); unregister_netdev(priv->dev); diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 13f4aa7593c8..7cecb16d3d48 100644 --- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -69,13 +69,28 @@ struct ipoib_mcast_iter { static void ipoib_mcast_free(struct ipoib_mcast *mcast) { struct net_device *dev = mcast->dev; + struct ipoib_dev_priv *priv = netdev_priv(dev); + struct ipoib_neigh *neigh, *tmp; int tx_dropped = 0; ipoib_dbg_mcast(netdev_priv(dev), "deleting multicast group %pI6\n", mcast->mcmember.mgid.raw); - /* remove all neigh connected to this mcast */ - ipoib_del_neighs_by_gid(dev, mcast->mcmember.mgid.raw); + spin_lock_irq(&priv->lock); + + list_for_each_entry_safe(neigh, tmp, &mcast->neigh_list, list) { + /* + * It's safe to call ipoib_put_ah() inside priv->lock + * here, because we know that mcast->ah will always + * hold one more reference, so ipoib_put_ah() will + * never do more than decrement the ref count. + */ + if (neigh->ah) + ipoib_put_ah(neigh->ah); + ipoib_neigh_free(dev, neigh); + } + + spin_unlock_irq(&priv->lock); if (mcast->ah) ipoib_put_ah(mcast->ah); @@ -640,12 +655,17 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast) return 0; } -void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb) +void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb) { struct ipoib_dev_priv *priv = netdev_priv(dev); + struct dst_entry *dst = skb_dst(skb); struct ipoib_mcast *mcast; + struct neighbour *n; unsigned long flags; - void *mgid = daddr + 4; + + n = NULL; + if (dst) + n = dst_neigh_lookup_skb(dst, skb); spin_lock_irqsave(&priv->lock, flags); @@ -701,29 +721,28 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb) out: if (mcast && mcast->ah) { - struct ipoib_neigh *neigh; - - spin_unlock_irqrestore(&priv->lock, flags); - neigh = ipoib_neigh_get(dev, daddr); - spin_lock_irqsave(&priv->lock, flags); - if (!neigh) { - spin_unlock_irqrestore(&priv->lock, flags); - neigh = ipoib_neigh_alloc(daddr, dev); - spin_lock_irqsave(&priv->lock, flags); - if (neigh) { - kref_get(&mcast->ah->ref); - neigh->ah = mcast->ah; - list_add_tail(&neigh->list, &mcast->neigh_list); + if (n) { + if (!*to_ipoib_neigh(n)) { + struct ipoib_neigh *neigh; + + neigh = ipoib_neigh_alloc(n, skb->dev); + if (neigh) { + kref_get(&mcast->ah->ref); + neigh->ah = mcast->ah; + list_add_tail(&neigh->list, + &mcast->neigh_list); + } } + neigh_release(n); } spin_unlock_irqrestore(&priv->lock, flags); ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN); - if (neigh) - ipoib_neigh_put(neigh); return; } unlock: + if (n) + neigh_release(n); spin_unlock_irqrestore(&priv->lock, flags); } diff --git a/trunk/drivers/input/serio/hp_sdc.c b/trunk/drivers/input/serio/hp_sdc.c index d7a7e54f6465..09a089996ded 100644 --- a/trunk/drivers/input/serio/hp_sdc.c +++ b/trunk/drivers/input/serio/hp_sdc.c @@ -878,7 +878,7 @@ static int __init hp_sdc_init(void) #endif errstr = "IRQ not available for"; - if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED, + if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM, "HP SDC", &hp_sdc)) goto err1; diff --git a/trunk/drivers/isdn/hardware/mISDN/avmfritz.c b/trunk/drivers/isdn/hardware/mISDN/avmfritz.c index fa6ca4733725..c08fc605e56b 100644 --- a/trunk/drivers/isdn/hardware/mISDN/avmfritz.c +++ b/trunk/drivers/isdn/hardware/mISDN/avmfritz.c @@ -449,8 +449,7 @@ hdlc_fill_fifo(struct bchannel *bch) { struct fritzcard *fc = bch->hw; struct hdlc_hw *hdlc; - int count, fs, cnt = 0, idx; - bool fillempty = false; + int count, fs, cnt = 0, idx, fillempty = 0; u8 *p; u32 *ptr, val, addr; @@ -463,7 +462,7 @@ hdlc_fill_fifo(struct bchannel *bch) return; count = fs; p = bch->fill; - fillempty = true; + fillempty = 1; } else { count = bch->tx_skb->len - bch->tx_idx; if (count <= 0) @@ -478,7 +477,7 @@ hdlc_fill_fifo(struct bchannel *bch) hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; } ptr = (u32 *)p; - if (!fillempty) { + if (fillempty) { pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count, bch->tx_idx, bch->tx_skb->len); bch->tx_idx += count; diff --git a/trunk/drivers/md/dm-raid.c b/trunk/drivers/md/dm-raid.c index 982e3e390c45..f2f29c526544 100644 --- a/trunk/drivers/md/dm-raid.c +++ b/trunk/drivers/md/dm-raid.c @@ -11,7 +11,6 @@ #include "md.h" #include "raid1.h" #include "raid5.h" -#include "raid10.h" #include "bitmap.h" #include @@ -53,10 +52,7 @@ struct raid_dev { #define DMPF_MAX_RECOVERY_RATE 0x20 #define DMPF_MAX_WRITE_BEHIND 0x40 #define DMPF_STRIPE_CACHE 0x80 -#define DMPF_REGION_SIZE 0x100 -#define DMPF_RAID10_COPIES 0x200 -#define DMPF_RAID10_FORMAT 0x400 - +#define DMPF_REGION_SIZE 0X100 struct raid_set { struct dm_target *ti; @@ -80,7 +76,6 @@ static struct raid_type { const unsigned algorithm; /* RAID algorithm. */ } raid_types[] = { {"raid1", "RAID1 (mirroring)", 0, 2, 1, 0 /* NONE */}, - {"raid10", "RAID10 (striped mirrors)", 0, 2, 10, UINT_MAX /* Varies */}, {"raid4", "RAID4 (dedicated parity disk)", 1, 2, 5, ALGORITHM_PARITY_0}, {"raid5_la", "RAID5 (left asymmetric)", 1, 2, 5, ALGORITHM_LEFT_ASYMMETRIC}, {"raid5_ra", "RAID5 (right asymmetric)", 1, 2, 5, ALGORITHM_RIGHT_ASYMMETRIC}, @@ -91,17 +86,6 @@ static struct raid_type { {"raid6_nc", "RAID6 (N continue)", 2, 4, 6, ALGORITHM_ROTATING_N_CONTINUE} }; -static unsigned raid10_md_layout_to_copies(int layout) -{ - return layout & 0xFF; -} - -static int raid10_format_to_md_layout(char *format, unsigned copies) -{ - /* 1 "far" copy, and 'copies' "near" copies */ - return (1 << 8) | (copies & 0xFF); -} - static struct raid_type *get_raid_type(char *name) { int i; @@ -355,16 +339,10 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size) * [max_write_behind ] See '-write-behind=' (man mdadm) * [stripe_cache ] Stripe cache size for higher RAIDs * [region_size ] Defines granularity of bitmap - * - * RAID10-only options: - * [raid10_copies <# copies>] Number of copies. (Default: 2) - * [raid10_format ] Layout algorithm. (Default: near) */ static int parse_raid_params(struct raid_set *rs, char **argv, unsigned num_raid_params) { - char *raid10_format = "near"; - unsigned raid10_copies = 2; unsigned i, rebuild_cnt = 0; unsigned long value, region_size = 0; sector_t sectors_per_dev = rs->ti->len; @@ -438,28 +416,11 @@ static int parse_raid_params(struct raid_set *rs, char **argv, } key = argv[i++]; - - /* Parameters that take a string value are checked here. */ - if (!strcasecmp(key, "raid10_format")) { - if (rs->raid_type->level != 10) { - rs->ti->error = "'raid10_format' is an invalid parameter for this RAID type"; - return -EINVAL; - } - if (strcmp("near", argv[i])) { - rs->ti->error = "Invalid 'raid10_format' value given"; - return -EINVAL; - } - raid10_format = argv[i]; - rs->print_flags |= DMPF_RAID10_FORMAT; - continue; - } - if (strict_strtoul(argv[i], 10, &value) < 0) { rs->ti->error = "Bad numerical argument given in raid params"; return -EINVAL; } - /* Parameters that take a numeric value are checked here */ if (!strcasecmp(key, "rebuild")) { rebuild_cnt++; @@ -478,7 +439,6 @@ static int parse_raid_params(struct raid_set *rs, char **argv, return -EINVAL; } break; - case 10: default: DMERR("The rebuild parameter is not supported for %s", rs->raid_type->name); rs->ti->error = "Rebuild not supported for this RAID type"; @@ -535,8 +495,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv, */ value /= 2; - if ((rs->raid_type->level != 5) && - (rs->raid_type->level != 6)) { + if (rs->raid_type->level < 5) { rs->ti->error = "Inappropriate argument: stripe_cache"; return -EINVAL; } @@ -561,14 +520,6 @@ static int parse_raid_params(struct raid_set *rs, char **argv, } else if (!strcasecmp(key, "region_size")) { rs->print_flags |= DMPF_REGION_SIZE; region_size = value; - } else if (!strcasecmp(key, "raid10_copies") && - (rs->raid_type->level == 10)) { - if ((value < 2) || (value > 0xFF)) { - rs->ti->error = "Bad value for 'raid10_copies'"; - return -EINVAL; - } - rs->print_flags |= DMPF_RAID10_COPIES; - raid10_copies = value; } else { DMERR("Unable to parse RAID parameter: %s", key); rs->ti->error = "Unable to parse RAID parameters"; @@ -587,22 +538,8 @@ static int parse_raid_params(struct raid_set *rs, char **argv, if (dm_set_target_max_io_len(rs->ti, max_io_len)) return -EINVAL; - if (rs->raid_type->level == 10) { - if (raid10_copies > rs->md.raid_disks) { - rs->ti->error = "Not enough devices to satisfy specification"; - return -EINVAL; - } - - /* (Len * #mirrors) / #devices */ - sectors_per_dev = rs->ti->len * raid10_copies; - sector_div(sectors_per_dev, rs->md.raid_disks); - - rs->md.layout = raid10_format_to_md_layout(raid10_format, - raid10_copies); - rs->md.new_layout = rs->md.layout; - } else if ((rs->raid_type->level > 1) && - sector_div(sectors_per_dev, - (rs->md.raid_disks - rs->raid_type->parity_devs))) { + if ((rs->raid_type->level > 1) && + sector_div(sectors_per_dev, (rs->md.raid_disks - rs->raid_type->parity_devs))) { rs->ti->error = "Target length not divisible by number of data devices"; return -EINVAL; } @@ -629,9 +566,6 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits) if (rs->raid_type->level == 1) return md_raid1_congested(&rs->md, bits); - if (rs->raid_type->level == 10) - return md_raid10_congested(&rs->md, bits); - return md_raid5_congested(&rs->md, bits); } @@ -950,9 +884,6 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs) case 6: redundancy = rs->raid_type->parity_devs; break; - case 10: - redundancy = raid10_md_layout_to_copies(mddev->layout) - 1; - break; default: ti->error = "Unknown RAID type"; return -EINVAL; @@ -1118,19 +1049,12 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv) goto bad; } - if (ti->len != rs->md.array_sectors) { - ti->error = "Array size does not match requested target length"; - ret = -EINVAL; - goto size_mismatch; - } rs->callbacks.congested_fn = raid_is_congested; dm_table_add_target_callbacks(ti->table, &rs->callbacks); mddev_suspend(&rs->md); return 0; -size_mismatch: - md_stop(&rs->md); bad: context_free(rs); @@ -1279,13 +1203,6 @@ static int raid_status(struct dm_target *ti, status_type_t type, DMEMIT(" region_size %lu", rs->md.bitmap_info.chunksize >> 9); - if (rs->print_flags & DMPF_RAID10_COPIES) - DMEMIT(" raid10_copies %u", - raid10_md_layout_to_copies(rs->md.layout)); - - if (rs->print_flags & DMPF_RAID10_FORMAT) - DMEMIT(" raid10_format near"); - DMEMIT(" %d", rs->md.raid_disks); for (i = 0; i < rs->md.raid_disks; i++) { if (rs->dev[i].meta_dev) @@ -1360,7 +1277,7 @@ static void raid_resume(struct dm_target *ti) static struct target_type raid_target = { .name = "raid", - .version = {1, 3, 0}, + .version = {1, 2, 0}, .module = THIS_MODULE, .ctr = raid_ctr, .dtr = raid_dtr, @@ -1387,8 +1304,6 @@ module_init(dm_raid_init); module_exit(dm_raid_exit); MODULE_DESCRIPTION(DM_NAME " raid4/5/6 target"); -MODULE_ALIAS("dm-raid1"); -MODULE_ALIAS("dm-raid10"); MODULE_ALIAS("dm-raid4"); MODULE_ALIAS("dm-raid5"); MODULE_ALIAS("dm-raid6"); diff --git a/trunk/drivers/md/md.c b/trunk/drivers/md/md.c index fcd098794d37..d5ab4493c8be 100644 --- a/trunk/drivers/md/md.c +++ b/trunk/drivers/md/md.c @@ -498,13 +498,61 @@ void md_flush_request(struct mddev *mddev, struct bio *bio) } EXPORT_SYMBOL(md_flush_request); -void md_unplug(struct blk_plug_cb *cb, bool from_schedule) +/* Support for plugging. + * This mirrors the plugging support in request_queue, but does not + * require having a whole queue or request structures. + * We allocate an md_plug_cb for each md device and each thread it gets + * plugged on. This links tot the private plug_handle structure in the + * personality data where we keep a count of the number of outstanding + * plugs so other code can see if a plug is active. + */ +struct md_plug_cb { + struct blk_plug_cb cb; + struct mddev *mddev; +}; + +static void plugger_unplug(struct blk_plug_cb *cb) { - struct mddev *mddev = cb->data; - md_wakeup_thread(mddev->thread); - kfree(cb); + struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb); + if (atomic_dec_and_test(&mdcb->mddev->plug_cnt)) + md_wakeup_thread(mdcb->mddev->thread); + kfree(mdcb); } -EXPORT_SYMBOL(md_unplug); + +/* Check that an unplug wakeup will come shortly. + * If not, wakeup the md thread immediately + */ +int mddev_check_plugged(struct mddev *mddev) +{ + struct blk_plug *plug = current->plug; + struct md_plug_cb *mdcb; + + if (!plug) + return 0; + + list_for_each_entry(mdcb, &plug->cb_list, cb.list) { + if (mdcb->cb.callback == plugger_unplug && + mdcb->mddev == mddev) { + /* Already on the list, move to top */ + if (mdcb != list_first_entry(&plug->cb_list, + struct md_plug_cb, + cb.list)) + list_move(&mdcb->cb.list, &plug->cb_list); + return 1; + } + } + /* Not currently on the callback list */ + mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC); + if (!mdcb) + return 0; + + mdcb->mddev = mddev; + mdcb->cb.callback = plugger_unplug; + atomic_inc(&mddev->plug_cnt); + list_add(&mdcb->cb.list, &plug->cb_list); + return 1; +} +EXPORT_SYMBOL_GPL(mddev_check_plugged); static inline struct mddev *mddev_get(struct mddev *mddev) { @@ -554,6 +602,7 @@ void mddev_init(struct mddev *mddev) atomic_set(&mddev->active, 1); atomic_set(&mddev->openers, 0); atomic_set(&mddev->active_io, 0); + atomic_set(&mddev->plug_cnt, 0); spin_lock_init(&mddev->write_lock); atomic_set(&mddev->flush_pending, 0); init_waitqueue_head(&mddev->sb_wait); @@ -3893,13 +3942,17 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len) break; case clear: /* stopping an active array */ + if (atomic_read(&mddev->openers) > 0) + return -EBUSY; err = do_md_stop(mddev, 0, NULL); break; case inactive: /* stopping an active array */ - if (mddev->pers) + if (mddev->pers) { + if (atomic_read(&mddev->openers) > 0) + return -EBUSY; err = do_md_stop(mddev, 2, NULL); - else + } else err = 0; /* already inactive */ break; case suspended: diff --git a/trunk/drivers/md/md.h b/trunk/drivers/md/md.h index f385b038589d..7b4a3c318cae 100644 --- a/trunk/drivers/md/md.h +++ b/trunk/drivers/md/md.h @@ -266,6 +266,9 @@ struct mddev { int new_chunk_sectors; int reshape_backwards; + atomic_t plug_cnt; /* If device is expecting + * more bios soon. + */ struct md_thread *thread; /* management thread */ struct md_thread *sync_thread; /* doing resync or reconstruct */ sector_t curr_resync; /* last block scheduled */ @@ -627,12 +630,6 @@ extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask, struct mddev *mddev); extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs, struct mddev *mddev); +extern int mddev_check_plugged(struct mddev *mddev); extern void md_trim_bio(struct bio *bio, int offset, int size); - -extern void md_unplug(struct blk_plug_cb *cb, bool from_schedule); -static inline int mddev_check_plugged(struct mddev *mddev) -{ - return !!blk_check_plugged(md_unplug, mddev, - sizeof(struct blk_plug_cb)); -} #endif /* _MD_MD_H */ diff --git a/trunk/drivers/md/raid1.c b/trunk/drivers/md/raid1.c index 9f7f8bee8442..cacd008d6864 100644 --- a/trunk/drivers/md/raid1.c +++ b/trunk/drivers/md/raid1.c @@ -46,20 +46,6 @@ */ #define NR_RAID1_BIOS 256 -/* when we get a read error on a read-only array, we redirect to another - * device without failing the first device, or trying to over-write to - * correct the read error. To keep track of bad blocks on a per-bio - * level, we store IO_BLOCKED in the appropriate 'bios' pointer - */ -#define IO_BLOCKED ((struct bio *)1) -/* When we successfully write to a known bad-block, we need to remove the - * bad-block marking which must be done from process context. So we record - * the success by setting devs[n].bio to IO_MADE_GOOD - */ -#define IO_MADE_GOOD ((struct bio *)2) - -#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2) - /* When there are this many requests queue to be written by * the raid1 thread, we become 'congested' to provide back-pressure * for writeback. @@ -497,14 +483,12 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect const sector_t this_sector = r1_bio->sector; int sectors; int best_good_sectors; - int best_disk, best_dist_disk, best_pending_disk; - int has_nonrot_disk; - int disk; + int start_disk; + int best_disk; + int i; sector_t best_dist; - unsigned int min_pending; struct md_rdev *rdev; int choose_first; - int choose_next_idle; rcu_read_lock(); /* @@ -515,26 +499,26 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect retry: sectors = r1_bio->sectors; best_disk = -1; - best_dist_disk = -1; best_dist = MaxSector; - best_pending_disk = -1; - min_pending = UINT_MAX; best_good_sectors = 0; - has_nonrot_disk = 0; - choose_next_idle = 0; if (conf->mddev->recovery_cp < MaxSector && - (this_sector + sectors >= conf->next_resync)) + (this_sector + sectors >= conf->next_resync)) { choose_first = 1; - else + start_disk = 0; + } else { choose_first = 0; + start_disk = conf->last_used; + } - for (disk = 0 ; disk < conf->raid_disks * 2 ; disk++) { + for (i = 0 ; i < conf->raid_disks * 2 ; i++) { sector_t dist; sector_t first_bad; int bad_sectors; - unsigned int pending; - bool nonrot; + + int disk = start_disk + i; + if (disk >= conf->raid_disks * 2) + disk -= conf->raid_disks * 2; rdev = rcu_dereference(conf->mirrors[disk].rdev); if (r1_bio->bios[disk] == IO_BLOCKED @@ -593,77 +577,22 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect } else best_good_sectors = sectors; - nonrot = blk_queue_nonrot(bdev_get_queue(rdev->bdev)); - has_nonrot_disk |= nonrot; - pending = atomic_read(&rdev->nr_pending); dist = abs(this_sector - conf->mirrors[disk].head_position); - if (choose_first) { - best_disk = disk; - break; - } - /* Don't change to another disk for sequential reads */ - if (conf->mirrors[disk].next_seq_sect == this_sector - || dist == 0) { - int opt_iosize = bdev_io_opt(rdev->bdev) >> 9; - struct raid1_info *mirror = &conf->mirrors[disk]; - - best_disk = disk; - /* - * If buffered sequential IO size exceeds optimal - * iosize, check if there is idle disk. If yes, choose - * the idle disk. read_balance could already choose an - * idle disk before noticing it's a sequential IO in - * this disk. This doesn't matter because this disk - * will idle, next time it will be utilized after the - * first disk has IO size exceeds optimal iosize. In - * this way, iosize of the first disk will be optimal - * iosize at least. iosize of the second disk might be - * small, but not a big deal since when the second disk - * starts IO, the first disk is likely still busy. - */ - if (nonrot && opt_iosize > 0 && - mirror->seq_start != MaxSector && - mirror->next_seq_sect > opt_iosize && - mirror->next_seq_sect - opt_iosize >= - mirror->seq_start) { - choose_next_idle = 1; - continue; - } - break; - } - /* If device is idle, use it */ - if (pending == 0) { + if (choose_first + /* Don't change to another disk for sequential reads */ + || conf->next_seq_sect == this_sector + || dist == 0 + /* If device is idle, use it */ + || atomic_read(&rdev->nr_pending) == 0) { best_disk = disk; break; } - - if (choose_next_idle) - continue; - - if (min_pending > pending) { - min_pending = pending; - best_pending_disk = disk; - } - if (dist < best_dist) { best_dist = dist; - best_dist_disk = disk; + best_disk = disk; } } - /* - * If all disks are rotational, choose the closest disk. If any disk is - * non-rotational, choose the disk with less pending request even the - * disk is rotational, which might/might not be optimal for raids with - * mixed ratation/non-rotational disks depending on workload. - */ - if (best_disk == -1) { - if (has_nonrot_disk) - best_disk = best_pending_disk; - else - best_disk = best_dist_disk; - } - if (best_disk >= 0) { rdev = rcu_dereference(conf->mirrors[best_disk].rdev); if (!rdev) @@ -677,11 +606,8 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect goto retry; } sectors = best_good_sectors; - - if (conf->mirrors[best_disk].next_seq_sect != this_sector) - conf->mirrors[best_disk].seq_start = this_sector; - - conf->mirrors[best_disk].next_seq_sect = this_sector + sectors; + conf->next_seq_sect = this_sector + sectors; + conf->last_used = best_disk; } rcu_read_unlock(); *max_sectors = sectors; @@ -947,7 +873,7 @@ static void alloc_behind_pages(struct bio *bio, struct r1bio *r1_bio) static void make_request(struct mddev *mddev, struct bio * bio) { struct r1conf *conf = mddev->private; - struct raid1_info *mirror; + struct mirror_info *mirror; struct r1bio *r1_bio; struct bio *read_bio; int i, disks; @@ -1438,7 +1364,7 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev) struct r1conf *conf = mddev->private; int err = -EEXIST; int mirror = 0; - struct raid1_info *p; + struct mirror_info *p; int first = 0; int last = conf->raid_disks - 1; struct request_queue *q = bdev_get_queue(rdev->bdev); @@ -1507,7 +1433,7 @@ static int raid1_remove_disk(struct mddev *mddev, struct md_rdev *rdev) struct r1conf *conf = mddev->private; int err = 0; int number = rdev->raid_disk; - struct raid1_info *p = conf->mirrors + number; + struct mirror_info *p = conf->mirrors+ number; if (rdev != p->rdev) p = conf->mirrors + conf->raid_disks + number; @@ -2247,7 +2173,8 @@ static void raid1d(struct mddev *mddev) blk_start_plug(&plug); for (;;) { - flush_pending_writes(conf); + if (atomic_read(&mddev->plug_cnt) == 0) + flush_pending_writes(conf); spin_lock_irqsave(&conf->device_lock, flags); if (list_empty(head)) { @@ -2444,18 +2371,6 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp bio->bi_rw = READ; bio->bi_end_io = end_sync_read; read_targets++; - } else if (!test_bit(WriteErrorSeen, &rdev->flags) && - test_bit(MD_RECOVERY_SYNC, &mddev->recovery) && - !test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) { - /* - * The device is suitable for reading (InSync), - * but has bad block(s) here. Let's try to correct them, - * if we are doing resync or repair. Otherwise, leave - * this device alone for this sync request. - */ - bio->bi_rw = WRITE; - bio->bi_end_io = end_sync_write; - write_targets++; } } if (bio->bi_end_io) { @@ -2513,10 +2428,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp /* There is nowhere to write, so all non-sync * drives must be failed - so we are finished */ - sector_t rv; - if (min_bad > 0) - max_sector = sector_nr + min_bad; - rv = max_sector - sector_nr; + sector_t rv = max_sector - sector_nr; *skipped = 1; put_buf(r1_bio); return rv; @@ -2609,7 +2521,7 @@ static struct r1conf *setup_conf(struct mddev *mddev) { struct r1conf *conf; int i; - struct raid1_info *disk; + struct mirror_info *disk; struct md_rdev *rdev; int err = -ENOMEM; @@ -2617,7 +2529,7 @@ static struct r1conf *setup_conf(struct mddev *mddev) if (!conf) goto abort; - conf->mirrors = kzalloc(sizeof(struct raid1_info) + conf->mirrors = kzalloc(sizeof(struct mirror_info) * mddev->raid_disks * 2, GFP_KERNEL); if (!conf->mirrors) @@ -2660,7 +2572,6 @@ static struct r1conf *setup_conf(struct mddev *mddev) mddev->merge_check_needed = 1; disk->head_position = 0; - disk->seq_start = MaxSector; } conf->raid_disks = mddev->raid_disks; conf->mddev = mddev; @@ -2674,6 +2585,7 @@ static struct r1conf *setup_conf(struct mddev *mddev) conf->recovery_disabled = mddev->recovery_disabled - 1; err = -EIO; + conf->last_used = -1; for (i = 0; i < conf->raid_disks * 2; i++) { disk = conf->mirrors + i; @@ -2699,9 +2611,19 @@ static struct r1conf *setup_conf(struct mddev *mddev) if (disk->rdev && (disk->rdev->saved_raid_disk < 0)) conf->fullsync = 1; - } + } else if (conf->last_used < 0) + /* + * The first working device is used as a + * starting point to read balancing. + */ + conf->last_used = i; } + if (conf->last_used < 0) { + printk(KERN_ERR "md/raid1:%s: no operational mirrors\n", + mdname(mddev)); + goto abort; + } err = -ENOMEM; conf->thread = md_register_thread(raid1d, mddev, "raid1"); if (!conf->thread) { @@ -2876,7 +2798,7 @@ static int raid1_reshape(struct mddev *mddev) */ mempool_t *newpool, *oldpool; struct pool_info *newpoolinfo; - struct raid1_info *newmirrors; + struct mirror_info *newmirrors; struct r1conf *conf = mddev->private; int cnt, raid_disks; unsigned long flags; @@ -2919,7 +2841,7 @@ static int raid1_reshape(struct mddev *mddev) kfree(newpoolinfo); return -ENOMEM; } - newmirrors = kzalloc(sizeof(struct raid1_info) * raid_disks * 2, + newmirrors = kzalloc(sizeof(struct mirror_info) * raid_disks * 2, GFP_KERNEL); if (!newmirrors) { kfree(newpoolinfo); @@ -2958,6 +2880,7 @@ static int raid1_reshape(struct mddev *mddev) conf->raid_disks = mddev->raid_disks = raid_disks; mddev->delta_disks = 0; + conf->last_used = 0; /* just make sure it is in-range */ lower_barrier(conf); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); diff --git a/trunk/drivers/md/raid1.h b/trunk/drivers/md/raid1.h index 0ff3715fb7eb..80ded139314c 100644 --- a/trunk/drivers/md/raid1.h +++ b/trunk/drivers/md/raid1.h @@ -1,15 +1,9 @@ #ifndef _RAID1_H #define _RAID1_H -struct raid1_info { +struct mirror_info { struct md_rdev *rdev; sector_t head_position; - - /* When choose the best device for a read (read_balance()) - * we try to keep sequential reads one the same device - */ - sector_t next_seq_sect; - sector_t seq_start; }; /* @@ -30,11 +24,17 @@ struct pool_info { struct r1conf { struct mddev *mddev; - struct raid1_info *mirrors; /* twice 'raid_disks' to + struct mirror_info *mirrors; /* twice 'raid_disks' to * allow for replacements. */ int raid_disks; + /* When choose the best device for a read (read_balance()) + * we try to keep sequential reads one the same device + * using 'last_used' and 'next_seq_sect' + */ + int last_used; + sector_t next_seq_sect; /* During resync, read_balancing is only allowed on the part * of the array that has been resynced. 'next_resync' tells us * where that is. @@ -135,6 +135,20 @@ struct r1bio { /* DO NOT PUT ANY NEW FIELDS HERE - bios array is contiguously alloced*/ }; +/* when we get a read error on a read-only array, we redirect to another + * device without failing the first device, or trying to over-write to + * correct the read error. To keep track of bad blocks on a per-bio + * level, we store IO_BLOCKED in the appropriate 'bios' pointer + */ +#define IO_BLOCKED ((struct bio *)1) +/* When we successfully write to a known bad-block, we need to remove the + * bad-block marking which must be done from process context. So we record + * the success by setting bios[n] to IO_MADE_GOOD + */ +#define IO_MADE_GOOD ((struct bio *)2) + +#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2) + /* bits for r1bio.state */ #define R1BIO_Uptodate 0 #define R1BIO_IsSync 1 diff --git a/trunk/drivers/md/raid10.c b/trunk/drivers/md/raid10.c index de5ed6fd8806..8da6282254c3 100644 --- a/trunk/drivers/md/raid10.c +++ b/trunk/drivers/md/raid10.c @@ -60,21 +60,7 @@ */ #define NR_RAID10_BIOS 256 -/* when we get a read error on a read-only array, we redirect to another - * device without failing the first device, or trying to over-write to - * correct the read error. To keep track of bad blocks on a per-bio - * level, we store IO_BLOCKED in the appropriate 'bios' pointer - */ -#define IO_BLOCKED ((struct bio *)1) -/* When we successfully write to a known bad-block, we need to remove the - * bad-block marking which must be done from process context. So we record - * the success by setting devs[n].bio to IO_MADE_GOOD - */ -#define IO_MADE_GOOD ((struct bio *)2) - -#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2) - -/* When there are this many requests queued to be written by +/* When there are this many requests queue to be written by * the raid10 thread, we become 'congested' to provide back-pressure * for writeback. */ @@ -731,7 +717,7 @@ static struct md_rdev *read_balance(struct r10conf *conf, int sectors = r10_bio->sectors; int best_good_sectors; sector_t new_distance, best_dist; - struct md_rdev *best_rdev, *rdev = NULL; + struct md_rdev *rdev, *best_rdev; int do_balance; int best_slot; struct geom *geo = &conf->geo; @@ -853,8 +839,9 @@ static struct md_rdev *read_balance(struct r10conf *conf, return rdev; } -int md_raid10_congested(struct mddev *mddev, int bits) +static int raid10_congested(void *data, int bits) { + struct mddev *mddev = data; struct r10conf *conf = mddev->private; int i, ret = 0; @@ -862,6 +849,8 @@ int md_raid10_congested(struct mddev *mddev, int bits) conf->pending_count >= max_queued_requests) return 1; + if (mddev_congested(mddev, bits)) + return 1; rcu_read_lock(); for (i = 0; (i < conf->geo.raid_disks || i < conf->prev.raid_disks) @@ -877,15 +866,6 @@ int md_raid10_congested(struct mddev *mddev, int bits) rcu_read_unlock(); return ret; } -EXPORT_SYMBOL_GPL(md_raid10_congested); - -static int raid10_congested(void *data, int bits) -{ - struct mddev *mddev = data; - - return mddev_congested(mddev, bits) || - md_raid10_congested(mddev, bits); -} static void flush_pending_writes(struct r10conf *conf) { @@ -1566,7 +1546,7 @@ static void error(struct mddev *mddev, struct md_rdev *rdev) static void print_conf(struct r10conf *conf) { int i; - struct raid10_info *tmp; + struct mirror_info *tmp; printk(KERN_DEBUG "RAID10 conf printout:\n"); if (!conf) { @@ -1600,7 +1580,7 @@ static int raid10_spare_active(struct mddev *mddev) { int i; struct r10conf *conf = mddev->private; - struct raid10_info *tmp; + struct mirror_info *tmp; int count = 0; unsigned long flags; @@ -1675,7 +1655,7 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev) else mirror = first; for ( ; mirror <= last ; mirror++) { - struct raid10_info *p = &conf->mirrors[mirror]; + struct mirror_info *p = &conf->mirrors[mirror]; if (p->recovery_disabled == mddev->recovery_disabled) continue; if (p->rdev) { @@ -1729,7 +1709,7 @@ static int raid10_remove_disk(struct mddev *mddev, struct md_rdev *rdev) int err = 0; int number = rdev->raid_disk; struct md_rdev **rdevp; - struct raid10_info *p = conf->mirrors + number; + struct mirror_info *p = conf->mirrors + number; print_conf(conf); if (rdev == p->rdev) @@ -2680,7 +2660,8 @@ static void raid10d(struct mddev *mddev) blk_start_plug(&plug); for (;;) { - flush_pending_writes(conf); + if (atomic_read(&mddev->plug_cnt) == 0) + flush_pending_writes(conf); spin_lock_irqsave(&conf->device_lock, flags); if (list_empty(head)) { @@ -2895,7 +2876,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, sector_t sect; int must_sync; int any_working; - struct raid10_info *mirror = &conf->mirrors[i]; + struct mirror_info *mirror = &conf->mirrors[i]; if ((mirror->rdev == NULL || test_bit(In_sync, &mirror->rdev->flags)) @@ -3407,7 +3388,7 @@ static struct r10conf *setup_conf(struct mddev *mddev) goto out; /* FIXME calc properly */ - conf->mirrors = kzalloc(sizeof(struct raid10_info)*(mddev->raid_disks + + conf->mirrors = kzalloc(sizeof(struct mirror_info)*(mddev->raid_disks + max(0,mddev->delta_disks)), GFP_KERNEL); if (!conf->mirrors) @@ -3471,7 +3452,7 @@ static int run(struct mddev *mddev) { struct r10conf *conf; int i, disk_idx, chunk_size; - struct raid10_info *disk; + struct mirror_info *disk; struct md_rdev *rdev; sector_t size; sector_t min_offset_diff = 0; @@ -3491,14 +3472,12 @@ static int run(struct mddev *mddev) conf->thread = NULL; chunk_size = mddev->chunk_sectors << 9; - if (mddev->queue) { - blk_queue_io_min(mddev->queue, chunk_size); - if (conf->geo.raid_disks % conf->geo.near_copies) - blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks); - else - blk_queue_io_opt(mddev->queue, chunk_size * - (conf->geo.raid_disks / conf->geo.near_copies)); - } + blk_queue_io_min(mddev->queue, chunk_size); + if (conf->geo.raid_disks % conf->geo.near_copies) + blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks); + else + blk_queue_io_opt(mddev->queue, chunk_size * + (conf->geo.raid_disks / conf->geo.near_copies)); rdev_for_each(rdev, mddev) { long long diff; @@ -3532,9 +3511,8 @@ static int run(struct mddev *mddev) if (first || diff < min_offset_diff) min_offset_diff = diff; - if (mddev->gendisk) - disk_stack_limits(mddev->gendisk, rdev->bdev, - rdev->data_offset << 9); + disk_stack_limits(mddev->gendisk, rdev->bdev, + rdev->data_offset << 9); disk->head_position = 0; } @@ -3597,22 +3575,22 @@ static int run(struct mddev *mddev) md_set_array_sectors(mddev, size); mddev->resync_max_sectors = size; - if (mddev->queue) { + mddev->queue->backing_dev_info.congested_fn = raid10_congested; + mddev->queue->backing_dev_info.congested_data = mddev; + + /* Calculate max read-ahead size. + * We need to readahead at least twice a whole stripe.... + * maybe... + */ + { int stripe = conf->geo.raid_disks * ((mddev->chunk_sectors << 9) / PAGE_SIZE); - mddev->queue->backing_dev_info.congested_fn = raid10_congested; - mddev->queue->backing_dev_info.congested_data = mddev; - - /* Calculate max read-ahead size. - * We need to readahead at least twice a whole stripe.... - * maybe... - */ stripe /= conf->geo.near_copies; if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe) mddev->queue->backing_dev_info.ra_pages = 2 * stripe; - blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec); } + blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec); if (md_integrity_register(mddev)) goto out_free_conf; @@ -3663,10 +3641,7 @@ static int stop(struct mddev *mddev) lower_barrier(conf); md_unregister_thread(&mddev->thread); - if (mddev->queue) - /* the unplug fn references 'conf'*/ - blk_sync_queue(mddev->queue); - + blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ if (conf->r10bio_pool) mempool_destroy(conf->r10bio_pool); kfree(conf->mirrors); @@ -3830,7 +3805,7 @@ static int raid10_check_reshape(struct mddev *mddev) if (mddev->delta_disks > 0) { /* allocate new 'mirrors' list */ conf->mirrors_new = kzalloc( - sizeof(struct raid10_info) + sizeof(struct mirror_info) *(mddev->raid_disks + mddev->delta_disks), GFP_KERNEL); @@ -3955,7 +3930,7 @@ static int raid10_start_reshape(struct mddev *mddev) spin_lock_irq(&conf->device_lock); if (conf->mirrors_new) { memcpy(conf->mirrors_new, conf->mirrors, - sizeof(struct raid10_info)*conf->prev.raid_disks); + sizeof(struct mirror_info)*conf->prev.raid_disks); smp_mb(); kfree(conf->mirrors_old); /* FIXME and elsewhere */ conf->mirrors_old = conf->mirrors; diff --git a/trunk/drivers/md/raid10.h b/trunk/drivers/md/raid10.h index 007c2c68dd83..135b1b0a1554 100644 --- a/trunk/drivers/md/raid10.h +++ b/trunk/drivers/md/raid10.h @@ -1,7 +1,7 @@ #ifndef _RAID10_H #define _RAID10_H -struct raid10_info { +struct mirror_info { struct md_rdev *rdev, *replacement; sector_t head_position; int recovery_disabled; /* matches @@ -13,8 +13,8 @@ struct raid10_info { struct r10conf { struct mddev *mddev; - struct raid10_info *mirrors; - struct raid10_info *mirrors_new, *mirrors_old; + struct mirror_info *mirrors; + struct mirror_info *mirrors_new, *mirrors_old; spinlock_t device_lock; /* geometry */ @@ -123,6 +123,20 @@ struct r10bio { } devs[0]; }; +/* when we get a read error on a read-only array, we redirect to another + * device without failing the first device, or trying to over-write to + * correct the read error. To keep track of bad blocks on a per-bio + * level, we store IO_BLOCKED in the appropriate 'bios' pointer + */ +#define IO_BLOCKED ((struct bio*)1) +/* When we successfully write to a known bad-block, we need to remove the + * bad-block marking which must be done from process context. So we record + * the success by setting devs[n].bio to IO_MADE_GOOD + */ +#define IO_MADE_GOOD ((struct bio *)2) + +#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2) + /* bits for r10bio.state */ enum r10bio_state { R10BIO_Uptodate, @@ -145,7 +159,4 @@ enum r10bio_state { */ R10BIO_Previous, }; - -extern int md_raid10_congested(struct mddev *mddev, int bits); - #endif diff --git a/trunk/drivers/md/raid5.c b/trunk/drivers/md/raid5.c index 87a2d0bdedd1..04348d76bb30 100644 --- a/trunk/drivers/md/raid5.c +++ b/trunk/drivers/md/raid5.c @@ -99,40 +99,34 @@ static inline struct bio *r5_next_bio(struct bio *bio, sector_t sector) * We maintain a biased count of active stripes in the bottom 16 bits of * bi_phys_segments, and a count of processed stripes in the upper 16 bits */ -static inline int raid5_bi_processed_stripes(struct bio *bio) +static inline int raid5_bi_phys_segments(struct bio *bio) { - atomic_t *segments = (atomic_t *)&bio->bi_phys_segments; - return (atomic_read(segments) >> 16) & 0xffff; + return bio->bi_phys_segments & 0xffff; } -static inline int raid5_dec_bi_active_stripes(struct bio *bio) +static inline int raid5_bi_hw_segments(struct bio *bio) { - atomic_t *segments = (atomic_t *)&bio->bi_phys_segments; - return atomic_sub_return(1, segments) & 0xffff; + return (bio->bi_phys_segments >> 16) & 0xffff; } -static inline void raid5_inc_bi_active_stripes(struct bio *bio) +static inline int raid5_dec_bi_phys_segments(struct bio *bio) { - atomic_t *segments = (atomic_t *)&bio->bi_phys_segments; - atomic_inc(segments); + --bio->bi_phys_segments; + return raid5_bi_phys_segments(bio); } -static inline void raid5_set_bi_processed_stripes(struct bio *bio, - unsigned int cnt) +static inline int raid5_dec_bi_hw_segments(struct bio *bio) { - atomic_t *segments = (atomic_t *)&bio->bi_phys_segments; - int old, new; + unsigned short val = raid5_bi_hw_segments(bio); - do { - old = atomic_read(segments); - new = (old & 0xffff) | (cnt << 16); - } while (atomic_cmpxchg(segments, old, new) != old); + --val; + bio->bi_phys_segments = (val << 16) | raid5_bi_phys_segments(bio); + return val; } -static inline void raid5_set_bi_stripes(struct bio *bio, unsigned int cnt) +static inline void raid5_set_bi_hw_segments(struct bio *bio, unsigned int cnt) { - atomic_t *segments = (atomic_t *)&bio->bi_phys_segments; - atomic_set(segments, cnt); + bio->bi_phys_segments = raid5_bi_phys_segments(bio) | (cnt << 16); } /* Find first data disk in a raid6 stripe */ @@ -196,56 +190,49 @@ static int stripe_operations_active(struct stripe_head *sh) test_bit(STRIPE_COMPUTE_RUN, &sh->state); } -static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh) +static void __release_stripe(struct r5conf *conf, struct stripe_head *sh) { - BUG_ON(!list_empty(&sh->lru)); - BUG_ON(atomic_read(&conf->active_stripes)==0); - if (test_bit(STRIPE_HANDLE, &sh->state)) { - if (test_bit(STRIPE_DELAYED, &sh->state) && - !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) - list_add_tail(&sh->lru, &conf->delayed_list); - else if (test_bit(STRIPE_BIT_DELAY, &sh->state) && - sh->bm_seq - conf->seq_write > 0) - list_add_tail(&sh->lru, &conf->bitmap_list); - else { - clear_bit(STRIPE_DELAYED, &sh->state); - clear_bit(STRIPE_BIT_DELAY, &sh->state); - list_add_tail(&sh->lru, &conf->handle_list); - } - md_wakeup_thread(conf->mddev->thread); - } else { - BUG_ON(stripe_operations_active(sh)); - if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) - if (atomic_dec_return(&conf->preread_active_stripes) - < IO_THRESHOLD) - md_wakeup_thread(conf->mddev->thread); - atomic_dec(&conf->active_stripes); - if (!test_bit(STRIPE_EXPANDING, &sh->state)) { - list_add_tail(&sh->lru, &conf->inactive_list); - wake_up(&conf->wait_for_stripe); - if (conf->retry_read_aligned) - md_wakeup_thread(conf->mddev->thread); + if (atomic_dec_and_test(&sh->count)) { + BUG_ON(!list_empty(&sh->lru)); + BUG_ON(atomic_read(&conf->active_stripes)==0); + if (test_bit(STRIPE_HANDLE, &sh->state)) { + if (test_bit(STRIPE_DELAYED, &sh->state) && + !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) + list_add_tail(&sh->lru, &conf->delayed_list); + else if (test_bit(STRIPE_BIT_DELAY, &sh->state) && + sh->bm_seq - conf->seq_write > 0) + list_add_tail(&sh->lru, &conf->bitmap_list); + else { + clear_bit(STRIPE_DELAYED, &sh->state); + clear_bit(STRIPE_BIT_DELAY, &sh->state); + list_add_tail(&sh->lru, &conf->handle_list); + } + md_wakeup_thread(conf->mddev->thread); + } else { + BUG_ON(stripe_operations_active(sh)); + if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) + if (atomic_dec_return(&conf->preread_active_stripes) + < IO_THRESHOLD) + md_wakeup_thread(conf->mddev->thread); + atomic_dec(&conf->active_stripes); + if (!test_bit(STRIPE_EXPANDING, &sh->state)) { + list_add_tail(&sh->lru, &conf->inactive_list); + wake_up(&conf->wait_for_stripe); + if (conf->retry_read_aligned) + md_wakeup_thread(conf->mddev->thread); + } } } } -static void __release_stripe(struct r5conf *conf, struct stripe_head *sh) -{ - if (atomic_dec_and_test(&sh->count)) - do_release_stripe(conf, sh); -} - static void release_stripe(struct stripe_head *sh) { struct r5conf *conf = sh->raid_conf; unsigned long flags; - local_irq_save(flags); - if (atomic_dec_and_lock(&sh->count, &conf->device_lock)) { - do_release_stripe(conf, sh); - spin_unlock(&conf->device_lock); - } - local_irq_restore(flags); + spin_lock_irqsave(&conf->device_lock, flags); + __release_stripe(conf, sh); + spin_unlock_irqrestore(&conf->device_lock, flags); } static inline void remove_hash(struct stripe_head *sh) @@ -653,9 +640,6 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s) else bi->bi_sector = (sh->sector + rdev->data_offset); - if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags)) - bi->bi_rw |= REQ_FLUSH; - bi->bi_flags = 1 << BIO_UPTODATE; bi->bi_idx = 0; bi->bi_io_vec[0].bv_len = STRIPE_SIZE; @@ -765,12 +749,14 @@ static void ops_complete_biofill(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; struct bio *return_bi = NULL; + struct r5conf *conf = sh->raid_conf; int i; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); /* clear completed biofills */ + spin_lock_irq(&conf->device_lock); for (i = sh->disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -788,7 +774,7 @@ static void ops_complete_biofill(void *stripe_head_ref) while (rbi && rbi->bi_sector < dev->sector + STRIPE_SECTORS) { rbi2 = r5_next_bio(rbi, dev->sector); - if (!raid5_dec_bi_active_stripes(rbi)) { + if (!raid5_dec_bi_phys_segments(rbi)) { rbi->bi_next = return_bi; return_bi = rbi; } @@ -796,6 +782,7 @@ static void ops_complete_biofill(void *stripe_head_ref) } } } + spin_unlock_irq(&conf->device_lock); clear_bit(STRIPE_BIOFILL_RUN, &sh->state); return_io(return_bi); @@ -807,6 +794,7 @@ static void ops_complete_biofill(void *stripe_head_ref) static void ops_run_biofill(struct stripe_head *sh) { struct dma_async_tx_descriptor *tx = NULL; + struct r5conf *conf = sh->raid_conf; struct async_submit_ctl submit; int i; @@ -817,10 +805,10 @@ static void ops_run_biofill(struct stripe_head *sh) struct r5dev *dev = &sh->dev[i]; if (test_bit(R5_Wantfill, &dev->flags)) { struct bio *rbi; - spin_lock_irq(&sh->stripe_lock); + spin_lock_irq(&conf->device_lock); dev->read = rbi = dev->toread; dev->toread = NULL; - spin_unlock_irq(&sh->stripe_lock); + spin_unlock_irq(&conf->device_lock); while (rbi && rbi->bi_sector < dev->sector + STRIPE_SECTORS) { tx = async_copy_data(0, rbi, dev->page, @@ -1156,12 +1144,12 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) { struct bio *wbi; - spin_lock_irq(&sh->stripe_lock); + spin_lock_irq(&sh->raid_conf->device_lock); chosen = dev->towrite; dev->towrite = NULL; BUG_ON(dev->written); wbi = dev->written = chosen; - spin_unlock_irq(&sh->stripe_lock); + spin_unlock_irq(&sh->raid_conf->device_lock); while (wbi && wbi->bi_sector < dev->sector + STRIPE_SECTORS) { @@ -1466,8 +1454,6 @@ static int grow_one_stripe(struct r5conf *conf) init_waitqueue_head(&sh->ops.wait_for_ops); #endif - spin_lock_init(&sh->stripe_lock); - if (grow_buffers(sh)) { shrink_buffers(sh); kmem_cache_free(conf->slab_cache, sh); @@ -1753,9 +1739,7 @@ static void raid5_end_read_request(struct bio * bi, int error) atomic_add(STRIPE_SECTORS, &rdev->corrected_errors); clear_bit(R5_ReadError, &sh->dev[i].flags); clear_bit(R5_ReWrite, &sh->dev[i].flags); - } else if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags)) - clear_bit(R5_ReadNoMerge, &sh->dev[i].flags); - + } if (atomic_read(&rdev->read_errors)) atomic_set(&rdev->read_errors, 0); } else { @@ -1800,11 +1784,7 @@ static void raid5_end_read_request(struct bio * bi, int error) else retry = 1; if (retry) - if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags)) { - set_bit(R5_ReadError, &sh->dev[i].flags); - clear_bit(R5_ReadNoMerge, &sh->dev[i].flags); - } else - set_bit(R5_ReadNoMerge, &sh->dev[i].flags); + set_bit(R5_ReadError, &sh->dev[i].flags); else { clear_bit(R5_ReadError, &sh->dev[i].flags); clear_bit(R5_ReWrite, &sh->dev[i].flags); @@ -2360,18 +2340,11 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in (unsigned long long)bi->bi_sector, (unsigned long long)sh->sector); - /* - * If several bio share a stripe. The bio bi_phys_segments acts as a - * reference count to avoid race. The reference count should already be - * increased before this function is called (for example, in - * make_request()), so other bio sharing this stripe will not free the - * stripe. If a stripe is owned by one stripe, the stripe lock will - * protect it. - */ - spin_lock_irq(&sh->stripe_lock); + + spin_lock_irq(&conf->device_lock); if (forwrite) { bip = &sh->dev[dd_idx].towrite; - if (*bip == NULL) + if (*bip == NULL && sh->dev[dd_idx].written == NULL) firstwrite = 1; } else bip = &sh->dev[dd_idx].toread; @@ -2387,7 +2360,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in if (*bip) bi->bi_next = *bip; *bip = bi; - raid5_inc_bi_active_stripes(bi); + bi->bi_phys_segments++; if (forwrite) { /* check if page is covered */ @@ -2402,7 +2375,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS) set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags); } - spin_unlock_irq(&sh->stripe_lock); + spin_unlock_irq(&conf->device_lock); pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n", (unsigned long long)(*bip)->bi_sector, @@ -2418,7 +2391,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in overlap: set_bit(R5_Overlap, &sh->dev[dd_idx].flags); - spin_unlock_irq(&sh->stripe_lock); + spin_unlock_irq(&conf->device_lock); return 0; } @@ -2468,11 +2441,10 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, rdev_dec_pending(rdev, conf->mddev); } } - spin_lock_irq(&sh->stripe_lock); + spin_lock_irq(&conf->device_lock); /* fail all writes first */ bi = sh->dev[i].towrite; sh->dev[i].towrite = NULL; - spin_unlock_irq(&sh->stripe_lock); if (bi) { s->to_write--; bitmap_end = 1; @@ -2485,17 +2457,13 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, sh->dev[i].sector + STRIPE_SECTORS) { struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector); clear_bit(BIO_UPTODATE, &bi->bi_flags); - if (!raid5_dec_bi_active_stripes(bi)) { + if (!raid5_dec_bi_phys_segments(bi)) { md_write_end(conf->mddev); bi->bi_next = *return_bi; *return_bi = bi; } bi = nextbi; } - if (bitmap_end) - bitmap_endwrite(conf->mddev->bitmap, sh->sector, - STRIPE_SECTORS, 0, 0); - bitmap_end = 0; /* and fail all 'written' */ bi = sh->dev[i].written; sh->dev[i].written = NULL; @@ -2504,7 +2472,7 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, sh->dev[i].sector + STRIPE_SECTORS) { struct bio *bi2 = r5_next_bio(bi, sh->dev[i].sector); clear_bit(BIO_UPTODATE, &bi->bi_flags); - if (!raid5_dec_bi_active_stripes(bi)) { + if (!raid5_dec_bi_phys_segments(bi)) { md_write_end(conf->mddev); bi->bi_next = *return_bi; *return_bi = bi; @@ -2528,13 +2496,14 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector); clear_bit(BIO_UPTODATE, &bi->bi_flags); - if (!raid5_dec_bi_active_stripes(bi)) { + if (!raid5_dec_bi_phys_segments(bi)) { bi->bi_next = *return_bi; *return_bi = bi; } bi = nextbi; } } + spin_unlock_irq(&conf->device_lock); if (bitmap_end) bitmap_endwrite(conf->mddev->bitmap, sh->sector, STRIPE_SECTORS, 0, 0); @@ -2738,23 +2707,30 @@ static void handle_stripe_clean_event(struct r5conf *conf, test_bit(R5_UPTODATE, &dev->flags)) { /* We can return any write requests */ struct bio *wbi, *wbi2; + int bitmap_end = 0; pr_debug("Return write for disc %d\n", i); + spin_lock_irq(&conf->device_lock); wbi = dev->written; dev->written = NULL; while (wbi && wbi->bi_sector < dev->sector + STRIPE_SECTORS) { wbi2 = r5_next_bio(wbi, dev->sector); - if (!raid5_dec_bi_active_stripes(wbi)) { + if (!raid5_dec_bi_phys_segments(wbi)) { md_write_end(conf->mddev); wbi->bi_next = *return_bi; *return_bi = wbi; } wbi = wbi2; } - bitmap_endwrite(conf->mddev->bitmap, sh->sector, - STRIPE_SECTORS, + if (dev->towrite == NULL) + bitmap_end = 1; + spin_unlock_irq(&conf->device_lock); + if (bitmap_end) + bitmap_endwrite(conf->mddev->bitmap, + sh->sector, + STRIPE_SECTORS, !test_bit(STRIPE_DEGRADED, &sh->state), - 0); + 0); } } @@ -3206,6 +3182,7 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s) /* Now to look around and see what can be done */ rcu_read_lock(); + spin_lock_irq(&conf->device_lock); for (i=disks; i--; ) { struct md_rdev *rdev; sector_t first_bad; @@ -3351,6 +3328,7 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s) do_recovery = 1; } } + spin_unlock_irq(&conf->device_lock); if (test_bit(STRIPE_SYNCING, &sh->state)) { /* If there is a failed device being replaced, * we must be recovering. @@ -3813,7 +3791,7 @@ static struct bio *remove_bio_from_retry(struct r5conf *conf) * this sets the active strip count to 1 and the processed * strip count to zero (upper 8 bits) */ - raid5_set_bi_stripes(bi, 1); /* biased count of active stripes */ + bi->bi_phys_segments = 1; /* biased count of active stripes */ } return bi; @@ -4135,7 +4113,7 @@ static void make_request(struct mddev *mddev, struct bio * bi) finish_wait(&conf->wait_for_overlap, &w); set_bit(STRIPE_HANDLE, &sh->state); clear_bit(STRIPE_DELAYED, &sh->state); - if ((bi->bi_rw & REQ_NOIDLE) && + if ((bi->bi_rw & REQ_SYNC) && !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) atomic_inc(&conf->preread_active_stripes); mddev_check_plugged(mddev); @@ -4148,7 +4126,9 @@ static void make_request(struct mddev *mddev, struct bio * bi) } } - remaining = raid5_dec_bi_active_stripes(bi); + spin_lock_irq(&conf->device_lock); + remaining = raid5_dec_bi_phys_segments(bi); + spin_unlock_irq(&conf->device_lock); if (remaining == 0) { if ( rw == WRITE ) @@ -4504,7 +4484,7 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio) sector += STRIPE_SECTORS, scnt++) { - if (scnt < raid5_bi_processed_stripes(raid_bio)) + if (scnt < raid5_bi_hw_segments(raid_bio)) /* already done this stripe */ continue; @@ -4512,24 +4492,25 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio) if (!sh) { /* failed to get a stripe - must wait */ - raid5_set_bi_processed_stripes(raid_bio, scnt); + raid5_set_bi_hw_segments(raid_bio, scnt); conf->retry_read_aligned = raid_bio; return handled; } if (!add_stripe_bio(sh, raid_bio, dd_idx, 0)) { release_stripe(sh); - raid5_set_bi_processed_stripes(raid_bio, scnt); + raid5_set_bi_hw_segments(raid_bio, scnt); conf->retry_read_aligned = raid_bio; return handled; } - set_bit(R5_ReadNoMerge, &sh->dev[dd_idx].flags); handle_stripe(sh); release_stripe(sh); handled++; } - remaining = raid5_dec_bi_active_stripes(raid_bio); + spin_lock_irq(&conf->device_lock); + remaining = raid5_dec_bi_phys_segments(raid_bio); + spin_unlock_irq(&conf->device_lock); if (remaining == 0) bio_endio(raid_bio, 0); if (atomic_dec_and_test(&conf->active_aligned_reads)) @@ -4562,7 +4543,7 @@ static void raid5d(struct mddev *mddev) while (1) { struct bio *bio; - if ( + if (atomic_read(&mddev->plug_cnt) == 0 && !list_empty(&conf->bitmap_list)) { /* Now is a good time to flush some bitmap updates */ conf->seq_flush++; @@ -4572,7 +4553,8 @@ static void raid5d(struct mddev *mddev) conf->seq_write = conf->seq_flush; activate_bit_delay(conf); } - raid5_activate_delayed(conf); + if (atomic_read(&mddev->plug_cnt) == 0) + raid5_activate_delayed(conf); while ((bio = remove_bio_from_retry(conf))) { int ok; diff --git a/trunk/drivers/md/raid5.h b/trunk/drivers/md/raid5.h index 61dbb615c30b..2164021f3b5f 100644 --- a/trunk/drivers/md/raid5.h +++ b/trunk/drivers/md/raid5.h @@ -210,7 +210,6 @@ struct stripe_head { int disks; /* disks in stripe */ enum check_states check_state; enum reconstruct_states reconstruct_state; - spinlock_t stripe_lock; /** * struct stripe_operations * @target - STRIPE_OP_COMPUTE_BLK target @@ -274,7 +273,6 @@ enum r5dev_flags { R5_Wantwrite, R5_Overlap, /* There is a pending overlapping request * on this block */ - R5_ReadNoMerge, /* prevent bio from merging in block-layer */ R5_ReadError, /* seen a read error here recently */ R5_ReWrite, /* have tried to over-write the readerror */ diff --git a/trunk/drivers/media/common/tuners/tuner-xc2028.c b/trunk/drivers/media/common/tuners/tuner-xc2028.c index ea0550eafe7d..f88f948efee2 100644 --- a/trunk/drivers/media/common/tuners/tuner-xc2028.c +++ b/trunk/drivers/media/common/tuners/tuner-xc2028.c @@ -756,7 +756,7 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type, * 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) - goto ret; + return rc; *afc = afc_reg * 15625; /* Hz */ diff --git a/trunk/drivers/media/common/tuners/xc5000.c b/trunk/drivers/media/common/tuners/xc5000.c index 362a8d7c9738..bac8009e1d49 100644 --- a/trunk/drivers/media/common/tuners/xc5000.c +++ b/trunk/drivers/media/common/tuners/xc5000.c @@ -210,15 +210,13 @@ 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 = XC5000A_FIRMWARE, + .name = "dvb-fe-xc5000-1.6.114.fw", .size = 12401, }; -#define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw" static const struct xc5000_fw_cfg xc5000c_41_024_5 = { - .name = XC5000C_FIRMWARE, + .name = "dvb-fe-xc5000c-41.024.5.fw", .size = 16497, }; @@ -1261,5 +1259,3 @@ 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); diff --git a/trunk/drivers/media/dvb/dvb-usb/az6007.c b/trunk/drivers/media/dvb/dvb-usb/az6007.c index 86861e6f86d2..8ffcad000ad3 100644 --- a/trunk/drivers/media/dvb/dvb-usb/az6007.c +++ b/trunk/drivers/media/dvb/dvb-usb/az6007.c @@ -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, 6); + memcpy(mac, st->data, sizeof(mac)); if (ret > 0) deb_info("%s: mac is %pM\n", __func__, mac); diff --git a/trunk/drivers/media/dvb/frontends/dib8000.c b/trunk/drivers/media/dvb/frontends/dib8000.c index 1f3bcb5a1de8..9ca34f495009 100644 --- a/trunk/drivers/media/dvb/frontends/dib8000.c +++ b/trunk/drivers/media/dvb/frontends/dib8000.c @@ -2680,14 +2680,12 @@ static int dib8000_tune(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; int ret = 0; - u16 lock, value, mode; + u16 lock, value, mode = fft_to_mode(state); // 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); diff --git a/trunk/drivers/media/dvb/frontends/lgs8gxx.c b/trunk/drivers/media/dvb/frontends/lgs8gxx.c index c2ea2749ebed..568363a10a31 100644 --- a/trunk/drivers/media/dvb/frontends/lgs8gxx.c +++ b/trunk/drivers/media/dvb/frontends/lgs8gxx.c @@ -40,8 +40,6 @@ 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)."); @@ -594,7 +592,7 @@ static int lgs8g75_init_data(struct lgs8gxx_state *priv) int rc; int i; - rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev); + rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev); if (rc) return rc; @@ -1072,4 +1070,3 @@ EXPORT_SYMBOL(lgs8gxx_attach); MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver"); MODULE_AUTHOR("David T. L. Wong "); MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(LGS8GXX_FIRMWARE); diff --git a/trunk/drivers/media/dvb/frontends/rtl2832.c b/trunk/drivers/media/dvb/frontends/rtl2832.c index 28269ccaeab7..2da592fb38ad 100644 --- a/trunk/drivers/media/dvb/frontends/rtl2832.c +++ b/trunk/drivers/media/dvb/frontends/rtl2832.c @@ -589,7 +589,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) return -EINVAL; } - for (j = 0; j < sizeof(bw_params[0]); j++) { + for (j = 0; j < sizeof(bw_params[j]); j++) { ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1); if (ret) goto err; diff --git a/trunk/drivers/media/dvb/siano/smscoreapi.c b/trunk/drivers/media/dvb/siano/smscoreapi.c index 9cc55546cc30..7331e8450d1a 100644 --- a/trunk/drivers/media/dvb/siano/smscoreapi.c +++ b/trunk/drivers/media/dvb/siano/smscoreapi.c @@ -276,13 +276,16 @@ 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 smscore_device_notifyee_t *elem; + struct list_head *next, *first; int rc = 0; /* note: must be called under g_deviceslock */ - list_for_each_entry(elem, &g_smscore_notifyees, entry) { - rc = elem->hotplug(coredev, device, arrival); + first = &g_smscore_notifyees; + + for (next = first->next; next != first; next = next->next) { + rc = ((struct smscore_device_notifyee_t *) next)-> + hotplug(coredev, device, arrival); if (rc < 0) break; } @@ -937,25 +940,29 @@ static struct smscore_client_t *smscore_find_client(struct smscore_device_t *coredev, int data_type, int id) { - struct list_head *first; - struct smscore_client_t *client; + struct smscore_client_t *client = NULL; + struct list_head *next, *first; unsigned long flags; - struct list_head *firstid; - struct smscore_idlist_t *client_id; + struct list_head *firstid, *nextid; + spin_lock_irqsave(&coredev->clientslock, flags); first = &coredev->clients; - 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; + 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; + } } } - client = NULL; -found: spin_unlock_irqrestore(&coredev->clientslock, flags); return client; } diff --git a/trunk/drivers/media/radio/Kconfig b/trunk/drivers/media/radio/Kconfig index 8090b87b3066..24ce5a47f955 100644 --- a/trunk/drivers/media/radio/Kconfig +++ b/trunk/drivers/media/radio/Kconfig @@ -57,39 +57,6 @@ 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 - . - - 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 - . - - 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" diff --git a/trunk/drivers/media/radio/Makefile b/trunk/drivers/media/radio/Makefile index c03ce4fe74e9..ca8c7d134b95 100644 --- a/trunk/drivers/media/radio/Makefile +++ b/trunk/drivers/media/radio/Makefile @@ -11,8 +11,6 @@ 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 @@ -31,6 +29,4 @@ 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 diff --git a/trunk/drivers/media/radio/radio-cadet.c b/trunk/drivers/media/radio/radio-cadet.c index 697a421c9940..16a089fad909 100644 --- a/trunk/drivers/media/radio/radio-cadet.c +++ b/trunk/drivers/media/radio/radio-cadet.c @@ -41,9 +41,6 @@ #include /* outb, outb_p */ #include #include -#include -#include -#include 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."); @@ -64,15 +61,14 @@ 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; - bool is_fm_band; - u32 curfreq; + int users; + int curtuner; 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; @@ -85,9 +81,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] = { - { 1835, 2621, 4128, 65535 }, - { 2185, 4369, 13107, 65535 }, +static __u16 sigtable[2][4] = { + { 5, 10, 30, 150 }, + { 28, 40, 63, 1000 } }; @@ -95,12 +91,14 @@ static int cadet_getstereo(struct cadet *dev) { int ret = V4L2_TUNER_SUB_MONO; - if (!dev->is_fm_band) /* Only FM has stereo capability! */ + if (dev->curtuner != 0) /* 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; } @@ -113,6 +111,8 @@ 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,6 +134,8 @@ static unsigned cadet_gettune(struct cadet *dev) * Restore volume/mute setting */ outb(curvol, dev->io + 1); + mutex_unlock(&dev->lock); + return fifo; } @@ -150,18 +152,20 @@ static unsigned cadet_getfreq(struct cadet *dev) /* * Convert to actual frequency */ - 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; + 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 */ } - freq -= 10700000; /* IF frequency is 10.7 MHz */ - freq = (freq * 16) / 1000; /* Make it 1/16 kHz */ + if (dev->curtuner == 1) /* AM */ + freq = ((fifo & 0x7fff) - 2010) * 16; + return freq; } @@ -170,6 +174,8 @@ 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 @@ -188,6 +194,7 @@ 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) @@ -196,14 +203,13 @@ 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->is_fm_band) { /* FM */ + if (dev->curtuner == 0) { /* FM */ test = 102400; - freq = freq / 16; /* Make it kHz */ + freq = (freq * 1000) / 16; /* Make it kHz */ freq += 10700; /* IF is 10700 kHz */ for (i = 0; i < 14; i++) { fifo = fifo << 1; @@ -213,17 +219,20 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq) } test = test >> 1; } - } else { /* AM */ - fifo = (freq / 16) + 450; /* Make it kHz */ - fifo |= 0x100000; /* Select AM Band */ + } + if (dev->curtuner == 1) { /* AM */ + fifo = (freq / 16) + 2010; /* 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 @@ -231,24 +240,49 @@ 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->is_fm_band][j]; - goto reset_rds; + dev->sigstrength = sigtable[dev->curtuner][j]; + return; } } 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; @@ -261,7 +295,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 + 1 == dev->rdsout) + if (dev->rdsin == dev->rdsout) printk(KERN_WARNING "cadet: RDS buffer overflow\n"); else dev->rdsin++; @@ -280,21 +314,11 @@ static void cadet_handler(unsigned long data) */ init_timer(&dev->readtimer); dev->readtimer.function = cadet_handler; - dev->readtimer.data = data; + dev->readtimer.data = (unsigned long)0; 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) { @@ -303,24 +327,28 @@ 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) - cadet_start_rds(dev); + 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->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++]; - - if (i && copy_to_user(data, readbuf, i)) - i = -EFAULT; -unlock: mutex_unlock(&dev->lock); + + if (copy_to_user(data, readbuf, i)) + return -EFAULT; return i; } @@ -331,58 +359,48 @@ 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->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | + v->capabilities = 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; - 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) { + 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 */ v->rxsubchans = cadet_getstereo(dev); - 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 { + 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; 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; } @@ -390,17 +408,11 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - return v->index ? -EINVAL : 0; -} + struct cadet *dev = video_drvdata(file); -static int vidioc_enum_freq_bands(struct file *file, void *priv, - struct v4l2_frequency_band *band) -{ - if (band->tuner) - return -EINVAL; - if (band->index >= ARRAY_SIZE(bands)) + if (v->index != 0 && v->index != 1) return -EINVAL; - *band = bands[band->index]; + dev->curtuner = v->index; return 0; } @@ -409,10 +421,9 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct cadet *dev = video_drvdata(file); - if (f->tuner) - return -EINVAL; + f->tuner = dev->curtuner; f->type = V4L2_TUNER_RADIO; - f->frequency = dev->curfreq; + f->frequency = cadet_getfreq(dev); return 0; } @@ -422,46 +433,103 @@ static int vidioc_s_frequency(struct file *file, void *priv, { struct cadet *dev = video_drvdata(file); - if (f->tuner) + 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)) 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 cadet_s_ctrl(struct v4l2_ctrl *ctrl) +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) { - struct cadet *dev = container_of(ctrl->handler, struct cadet, ctrl_handler); + 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); switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - outb(7, dev->io); /* Select tuner control */ - if (ctrl->val) - outb(0x00, dev->io + 1); + 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); else - outb(0x20, dev->io + 1); - return 0; + cadet_setvol(dev, 0xffff); + break; + case V4L2_CID_AUDIO_VOLUME: + cadet_setvol(dev, ctrl->value); + break; + default: + return -EINVAL; } - return -EINVAL; + 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; } static int cadet_open(struct file *file) { struct cadet *dev = video_drvdata(file); - int err; mutex_lock(&dev->lock); - err = v4l2_fh_open(file); - if (err) - goto fail; - if (v4l2_fh_is_singular_file(file)) + dev->users++; + if (1 == dev->users) init_waitqueue_head(&dev->read_queue); -fail: mutex_unlock(&dev->lock); - return err; + return 0; } static int cadet_release(struct file *file) @@ -469,11 +537,11 @@ static int cadet_release(struct file *file) struct cadet *dev = video_drvdata(file); mutex_lock(&dev->lock); - if (v4l2_fh_is_singular_file(file) && dev->rdsstat) { + dev->users--; + if (0 == dev->users) { del_timer_sync(&dev->readtimer); dev->rdsstat = 0; } - v4l2_fh_release(file); mutex_unlock(&dev->lock); return 0; } @@ -481,19 +549,11 @@ 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) - res |= POLLIN | POLLRDNORM; - return res; + return POLLIN | POLLRDNORM; + return 0; } @@ -512,14 +572,13 @@ 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_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, + .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, }; #ifdef CONFIG_PNP @@ -569,8 +628,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, bands[1].rangelow); - if (cadet_getfreq(dev) == bands[1].rangelow) { + cadet_setfreq(dev, 1410); + if (cadet_getfreq(dev) == 1410) { release_region(dev->io, 2); return; } @@ -589,8 +648,7 @@ static int __init cadet_init(void) { struct cadet *dev = &cadet_card; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - struct v4l2_ctrl_handler *hdl; - int res = -ENODEV; + int res; strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name)); mutex_init(&dev->lock); @@ -622,40 +680,23 @@ 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) - goto err_hdl; + if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { + v4l2_device_unregister(v4l2_dev); + release_region(dev->io, 2); + goto fail; + } 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 res; + return -ENODEV; } static void __exit cadet_exit(void) @@ -663,10 +704,7 @@ 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); } diff --git a/trunk/drivers/media/radio/radio-shark.c b/trunk/drivers/media/radio/radio-shark.c deleted file mode 100644 index d0b6bb507634..000000000000 --- a/trunk/drivers/media/radio/radio-shark.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - * 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Version Information - */ -MODULE_AUTHOR("Hans de Goede "); -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); diff --git a/trunk/drivers/media/radio/radio-shark2.c b/trunk/drivers/media/radio/radio-shark2.c deleted file mode 100644 index b9575de3e7e8..000000000000 --- a/trunk/drivers/media/radio/radio-shark2.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include "radio-tea5777.h" - -MODULE_AUTHOR("Hans de Goede "); -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); diff --git a/trunk/drivers/media/radio/radio-tea5777.c b/trunk/drivers/media/radio/radio-tea5777.c deleted file mode 100644 index 5bc9fa62720b..000000000000 --- a/trunk/drivers/media/radio/radio-tea5777.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * v4l2 driver for TEA5777 Philips AM/FM radio tuner chips - * - * Copyright (c) 2012 Hans de Goede - * - * Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips: - * - * Copyright (c) 2004 Jaroslav Kysela - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "radio-tea5777.h" - -MODULE_AUTHOR("Hans de Goede "); -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); diff --git a/trunk/drivers/media/radio/radio-tea5777.h b/trunk/drivers/media/radio/radio-tea5777.h deleted file mode 100644 index 55cbd78df5ed..000000000000 --- a/trunk/drivers/media/radio/radio-tea5777.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __RADIO_TEA5777_H -#define __RADIO_TEA5777_H - -/* - * v4l2 driver for TEA5777 Philips AM/FM radio tuner chips - * - * Copyright (c) 2012 Hans de Goede - * - * Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips: - * - * Copyright (c) 2004 Jaroslav Kysela - * Copyright (c) 2012 Hans de Goede - * - * 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 -#include -#include -#include - -#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 */ diff --git a/trunk/drivers/media/radio/si470x/radio-si470x-common.c b/trunk/drivers/media/radio/si470x/radio-si470x-common.c index 9e38132afec6..d485b79222fd 100644 --- a/trunk/drivers/media/radio/si470x/radio-si470x-common.c +++ b/trunk/drivers/media/radio/si470x/radio-si470x-common.c @@ -4,7 +4,6 @@ * Driver for radios with Silicon Labs Si470x FM Radio Receivers * * Copyright (c) 2009 Tobias Lorenz - * Copyright (c) 2012 Hans de Goede * * 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 @@ -128,6 +127,14 @@ 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) */ @@ -145,66 +152,19 @@ 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 */ @@ -214,12 +174,26 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) if (retval < 0) goto done; - /* 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; + /* 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)); + } if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) dev_warn(&radio->videodev.dev, "tune does not complete\n"); @@ -227,6 +201,7 @@ 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); @@ -235,39 +210,48 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) return retval; } + /* - * si470x_get_step - get channel spacing + * si470x_get_freq - get the frequency */ -static unsigned int si470x_get_step(struct si470x_device *radio) +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: - return 200 * 16; + spacing = 0.200 * FREQ_MUL; break; /* 1: 100 kHz (Europe, Japan) */ case 1: - return 100 * 16; + spacing = 0.100 * FREQ_MUL; break; /* 2: 50 kHz */ default: - return 50 * 16; + spacing = 0.050 * FREQ_MUL; break; }; -} - -/* - * si470x_get_freq - get the frequency - */ -static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) -{ - int chan, retval; + /* 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; + }; /* 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 * si470x_get_step(radio) + bands[radio->band].rangelow; + *freq = chan * spacing + band_bottom; return retval; } @@ -278,12 +262,44 @@ 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; - freq = clamp(freq, bands[radio->band].rangelow, - bands[radio->band].rangehigh); + /* 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); /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ - chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio); + chan = (freq - band_bottom) / spacing; return si470x_set_chan(radio, chan); } @@ -293,43 +309,19 @@ 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, - struct v4l2_hw_freq_seek *seek) + unsigned int wrap_around, unsigned int seek_upward) { - int band, retval; - unsigned int freq; + int retval = 0; + unsigned long timeout; 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 (seek->wrap_around) + if (wrap_around == 1) radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; else radio->registers[POWERCFG] |= POWERCFG_SKMODE; - if (seek->seek_upward) + if (seek_upward == 1) radio->registers[POWERCFG] |= POWERCFG_SEEKUP; else radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; @@ -337,12 +329,26 @@ static int si470x_set_seek(struct si470x_device *radio, if (retval < 0) return retval; - /* 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; + /* 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)); + } if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) dev_warn(&radio->videodev.dev, "seek does not complete\n"); @@ -350,6 +356,7 @@ 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); @@ -384,8 +391,8 @@ int si470x_start(struct si470x_device *radio) /* sysconfig 2 */ radio->registers[SYSCONFIG2] = - (0x1f << 8) | /* SEEKTH */ - ((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */ + (0x3f << 8) | /* SEEKTH */ + ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ 15; /* VOLUME (max) */ retval = si470x_set_register(radio, SYSCONFIG2); @@ -576,16 +583,14 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; + int retval; if (tuner->index != 0) return -EINVAL; - if (!radio->status_rssi_auto_update) { - retval = si470x_get_register(radio, STATUSRSSI); - if (retval < 0) - return retval; - } + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + return retval; /* driver constants */ strcpy(tuner->name, "FM"); @@ -594,8 +599,25 @@ 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; - tuner->rangelow = 76 * FREQ_MUL; - tuner->rangehigh = 108 * FREQ_MUL; + + /* 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; + }; /* stereo indicator == stereo (instead of mono) */ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) @@ -678,18 +700,10 @@ 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); } @@ -705,21 +719,7 @@ 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); -} - -/* - * 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; + return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); } const struct v4l2_ctrl_ops si470x_ctrl_ops = { @@ -736,7 +736,6 @@ 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, }; diff --git a/trunk/drivers/media/radio/si470x/radio-si470x-i2c.c b/trunk/drivers/media/radio/si470x/radio-si470x-i2c.c index 643a6ff7c5d0..a80044c5874e 100644 --- a/trunk/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/trunk/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -350,9 +350,7 @@ 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; @@ -408,6 +406,10 @@ 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) { diff --git a/trunk/drivers/media/radio/si470x/radio-si470x-usb.c b/trunk/drivers/media/radio/si470x/radio-si470x-usb.c index 146be4263ea1..f412f7ab270b 100644 --- a/trunk/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/trunk/drivers/media/radio/si470x/radio-si470x-usb.c @@ -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 1 +#define RADIO_SW_VERSION 7 #define RADIO_HW_VERSION 1 @@ -399,19 +399,12 @@ static void si470x_int_in_callback(struct urb *urb) } } - /* Sometimes the device returns len 0 packets */ - if (urb->actual_length != RDS_REPORT_SIZE) + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) goto resubmit; - 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)) { + if (urb->actual_length > 0) { /* Update RDS registers with URB data */ - for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++) + for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) radio->registers[STATUSRSSI + regnr] = get_unaligned_be16(&radio->int_in_buffer[ regnr * RADIO_REGISTER_SIZE + 1]); @@ -487,7 +480,6 @@ static void si470x_int_in_callback(struct urb *urb) radio->int_in_running = 0; } } - radio->status_rssi_auto_update = radio->int_in_running; } @@ -542,6 +534,13 @@ 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, @@ -561,15 +560,6 @@ 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; } @@ -597,9 +587,7 @@ 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; @@ -710,6 +698,9 @@ 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); @@ -732,9 +723,6 @@ 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); @@ -793,16 +781,11 @@ 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 */ - ret = si470x_start_usb(radio); - if (ret == 0) - v4l2_ctrl_handler_setup(&radio->hdl); - - return ret; + return si470x_start_usb(radio); } diff --git a/trunk/drivers/media/radio/si470x/radio-si470x.h b/trunk/drivers/media/radio/si470x/radio-si470x.h index 2f089b4252df..4921cab8e0fa 100644 --- a/trunk/drivers/media/radio/si470x/radio-si470x.h +++ b/trunk/drivers/media/radio/si470x/radio-si470x.h @@ -87,7 +87,7 @@ #define SYSCONFIG2 5 /* System Configuration 2 */ #define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */ -#define SYSCONFIG2_BAND 0x00c0 /* bits 07..06: Band Select */ +#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */ #define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */ #define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */ @@ -147,7 +147,6 @@ 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]; @@ -161,7 +160,7 @@ struct si470x_device { unsigned int wr_index; struct completion completion; - bool status_rssi_auto_update; /* Does RSSI get updated automatic? */ + bool stci_enabled; /* Seek/Tune Complete Interrupt */ #if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) /* reference to USB and video device */ @@ -190,7 +189,7 @@ struct si470x_device { * Firmware Versions **************************************************************************/ -#define RADIO_FW_VERSION 12 +#define RADIO_FW_VERSION 15 diff --git a/trunk/drivers/media/rc/Kconfig b/trunk/drivers/media/rc/Kconfig index 5180390be7ab..908ef70430e9 100644 --- a/trunk/drivers/media/rc/Kconfig +++ b/trunk/drivers/media/rc/Kconfig @@ -259,17 +259,6 @@ 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 diff --git a/trunk/drivers/media/rc/Makefile b/trunk/drivers/media/rc/Makefile index f871d1986c21..29f364f88a94 100644 --- a/trunk/drivers/media/rc/Makefile +++ b/trunk/drivers/media/rc/Makefile @@ -27,4 +27,3 @@ 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 diff --git a/trunk/drivers/media/rc/ati_remote.c b/trunk/drivers/media/rc/ati_remote.c index 8fa72e2dacb1..7be377fc1be8 100644 --- a/trunk/drivers/media/rc/ati_remote.c +++ b/trunk/drivers/media/rc/ati_remote.c @@ -147,8 +147,7 @@ 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) @@ -192,41 +191,17 @@ 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 */ }; @@ -321,8 +296,25 @@ 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) @@ -334,14 +326,12 @@ 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) { @@ -365,7 +355,7 @@ out: mutex_unlock(&ati_remote->open_mutex); } /* - * ati_remote_close + * ati_remote_close */ static void ati_remote_close(struct ati_remote *ati_remote) { @@ -400,7 +390,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) { @@ -418,12 +408,11 @@ 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; @@ -452,7 +441,7 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, } /* - * 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", @@ -489,7 +478,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) { @@ -529,8 +518,7 @@ 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; } @@ -558,9 +546,7 @@ 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; @@ -644,9 +630,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); @@ -673,8 +659,7 @@ 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); @@ -685,7 +670,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) { @@ -699,25 +684,22 @@ 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) @@ -744,7 +726,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) { @@ -843,10 +825,9 @@ 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; @@ -968,7 +949,7 @@ static int ati_remote_probe(struct usb_interface *interface, } /* - * ati_remote_disconnect + * ati_remote_disconnect */ static void ati_remote_disconnect(struct usb_interface *interface) { @@ -990,14 +971,6 @@ 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); diff --git a/trunk/drivers/media/rc/iguanair.c b/trunk/drivers/media/rc/iguanair.c deleted file mode 100644 index 5e2eaf8ba73e..000000000000 --- a/trunk/drivers/media/rc/iguanair.c +++ /dev/null @@ -1,639 +0,0 @@ -/* - * IguanaWorks USB IR Transceiver support - * - * Copyright (C) 2012 Sean Young - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#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 "); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(usb, iguanair_table); - diff --git a/trunk/drivers/media/rc/mceusb.c b/trunk/drivers/media/rc/mceusb.c index f38d9a8c6880..84e06d3aa696 100644 --- a/trunk/drivers/media/rc/mceusb.c +++ b/trunk/drivers/media/rc/mceusb.c @@ -199,7 +199,6 @@ 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 */ @@ -392,12 +391,6 @@ 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 */ { } }; @@ -417,12 +410,14 @@ 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, @@ -691,7 +686,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) +static void mce_async_callback(struct urb *urb, struct pt_regs *regs) { struct mceusb_dev *ir; int len; @@ -738,7 +733,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, mce_async_callback, + async_buf, size, (usb_complete_t)mce_async_callback, ir, ir->usb_ep_out->bInterval); memcpy(async_buf, data, size); @@ -1036,7 +1031,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) +static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) { struct mceusb_dev *ir; int buf_len; @@ -1336,6 +1331,7 @@ 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 @@ -1353,8 +1349,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, - mceusb_dev_recv, ir, ep_in->bInterval); + usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, + maxp, (usb_complete_t) 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; diff --git a/trunk/drivers/media/rc/rc-main.c b/trunk/drivers/media/rc/rc-main.c index cabc19c10515..6e16b09c24a9 100644 --- a/trunk/drivers/media/rc/rc-main.c +++ b/trunk/drivers/media/rc/rc-main.c @@ -775,11 +775,10 @@ 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 if (dev->raw) { + } else { 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, diff --git a/trunk/drivers/media/video/adv7180.c b/trunk/drivers/media/video/adv7180.c index 45ecf8db1eae..174bffacf117 100644 --- a/trunk/drivers/media/video/adv7180.c +++ b/trunk/drivers/media/video/adv7180.c @@ -26,10 +26,11 @@ #include #include #include -#include #include #include +#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 @@ -54,21 +55,21 @@ #define ADV7180_AUTODETECT_ENABLE_REG 0x07 #define ADV7180_AUTODETECT_DEFAULT 0x7f -/* Contrast */ + #define ADV7180_CON_REG 0x08 /*Unsigned */ -#define ADV7180_CON_MIN 0 -#define ADV7180_CON_DEF 128 -#define ADV7180_CON_MAX 255 -/* Brightness*/ +#define CON_REG_MIN 0 +#define CON_REG_DEF 128 +#define CON_REG_MAX 255 + #define ADV7180_BRI_REG 0x0a /*Signed */ -#define ADV7180_BRI_MIN -128 -#define ADV7180_BRI_DEF 0 -#define ADV7180_BRI_MAX 127 -/* Hue */ +#define BRI_REG_MIN -128 +#define BRI_REG_DEF 0 +#define BRI_REG_MAX 127 + #define ADV7180_HUE_REG 0x0b /*Signed, inverted */ -#define ADV7180_HUE_MIN -127 -#define ADV7180_HUE_DEF 0 -#define ADV7180_HUE_MAX 128 +#define HUE_REG_MIN -127 +#define HUE_REG_DEF 0 +#define HUE_REG_MAX 128 #define ADV7180_ADI_CTRL_REG 0x0e #define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 @@ -97,12 +98,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 ADV7180_SAT_MIN 0 -#define ADV7180_SAT_DEF 128 -#define ADV7180_SAT_MAX 255 +#define SAT_REG_MIN 0 +#define SAT_REG_DEF 128 +#define SAT_REG_MAX 255 #define ADV7180_IRQ1_LOCK 0x01 #define ADV7180_IRQ1_UNLOCK 0x02 @@ -120,18 +121,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) { @@ -236,7 +237,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) @@ -315,39 +316,117 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) return ret; } -static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) +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 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; + + 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) +{ + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); if (ret) return ret; - val = ctrl->val; + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val); + 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); 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, -val); + ret = i2c_smbus_write_byte_data(client, + ADV7180_HUE_REG, -state->hue); break; case V4L2_CID_CONTRAST: - ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val); + 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); 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 */ - ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, - val); + state->saturation = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_SD_SAT_CB_REG, + state->saturation); if (ret < 0) break; - ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, - val); + ret = i2c_smbus_write_byte_data(client, + ADV7180_SD_SAT_CR_REG, + state->saturation); break; default: ret = -EINVAL; @@ -357,42 +436,6 @@ static int adv7180_s_ctrl(struct v4l2_ctrl *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, @@ -402,9 +445,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 = v4l2_subdev_queryctrl, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = adv7180_queryctrl, + .g_ctrl = adv7180_g_ctrl, + .s_ctrl = adv7180_s_ctrl, }; static const struct v4l2_subdev_ops adv7180_ops = { @@ -496,7 +539,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, KBUILD_MODNAME, + ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, state); if (ret) return ret; @@ -537,6 +580,31 @@ 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; } @@ -564,26 +632,25 @@ 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 = adv7180_init_controls(state); - if (ret) - goto err_unreg_subdev; ret = init_device(client, state); - if (ret) - goto err_free_ctrl; + if (0 != ret) + goto err_unreg_subdev; 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 KBUILD_MODNAME ": Failed to probe: %d\n", ret); + printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret); return ret; } @@ -611,7 +678,7 @@ static __devexit int adv7180_remove(struct i2c_client *client) } static const struct i2c_device_id adv7180_id[] = { - {KBUILD_MODNAME, 0}, + {DRIVER_NAME, 0}, {}, }; @@ -649,7 +716,7 @@ MODULE_DEVICE_TABLE(i2c, adv7180_id); static struct i2c_driver adv7180_driver = { .driver = { .owner = THIS_MODULE, - .name = KBUILD_MODNAME, + .name = DRIVER_NAME, }, .probe = adv7180_probe, .remove = __devexit_p(adv7180_remove), diff --git a/trunk/drivers/media/video/bt8xx/bttv-cards.c b/trunk/drivers/media/video/bt8xx/bttv-cards.c index 38952faaffda..5f3a00c2c4f6 100644 --- a/trunk/drivers/media/video/bt8xx/bttv-cards.c +++ b/trunk/drivers/media/video/bt8xx/bttv-cards.c @@ -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,14 +2818,6 @@ 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); diff --git a/trunk/drivers/media/video/bt8xx/bttv.h b/trunk/drivers/media/video/bt8xx/bttv.h index 79a11240a590..acfe2f3b92d9 100644 --- a/trunk/drivers/media/video/bt8xx/bttv.h +++ b/trunk/drivers/media/video/bt8xx/bttv.h @@ -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 diff --git a/trunk/drivers/media/video/cx231xx/cx231xx-i2c.c b/trunk/drivers/media/video/cx231xx/cx231xx-i2c.c index 781feed406f7..925f3a04e53c 100644 --- a/trunk/drivers/media/video/cx231xx/cx231xx-i2c.c +++ b/trunk/drivers/media/video/cx231xx/cx231xx-i2c.c @@ -499,12 +499,16 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus) BUG_ON(!dev->cx231xx_send_usb_command); - bus->i2c_adap = cx231xx_adap_template; - bus->i2c_client = cx231xx_client_template; + 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.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); diff --git a/trunk/drivers/media/video/cx231xx/cx231xx.h b/trunk/drivers/media/video/cx231xx/cx231xx.h index a89d020de948..e17447554a0d 100644 --- a/trunk/drivers/media/video/cx231xx/cx231xx.h +++ b/trunk/drivers/media/video/cx231xx/cx231xx.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -480,6 +481,7 @@ 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; diff --git a/trunk/drivers/media/video/cx23885/cx23885-i2c.c b/trunk/drivers/media/video/cx23885/cx23885-i2c.c index 4887314339cb..be1e21d8295c 100644 --- a/trunk/drivers/media/video/cx23885/cx23885-i2c.c +++ b/trunk/drivers/media/video/cx23885/cx23885-i2c.c @@ -316,13 +316,19 @@ int cx23885_i2c_register(struct cx23885_i2c *bus) dprintk(1, "%s(bus = %d)\n", __func__, bus->nr); - bus->i2c_adap = cx23885_i2c_adap_template; - bus->i2c_client = cx23885_i2c_client_template; + 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.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); diff --git a/trunk/drivers/media/video/cx23885/cx23885.h b/trunk/drivers/media/video/cx23885/cx23885.h index 5d560c747e09..13c37ec07ae7 100644 --- a/trunk/drivers/media/video/cx23885/cx23885.h +++ b/trunk/drivers/media/video/cx23885/cx23885.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -246,6 +247,7 @@ 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; diff --git a/trunk/drivers/media/video/cx25821/cx25821-i2c.c b/trunk/drivers/media/video/cx25821/cx25821-i2c.c index 9844549764c9..6311180f430c 100644 --- a/trunk/drivers/media/video/cx25821/cx25821-i2c.c +++ b/trunk/drivers/media/video/cx25821/cx25821-i2c.c @@ -305,12 +305,18 @@ int cx25821_i2c_register(struct cx25821_i2c *bus) dprintk(1, "%s(bus = %d)\n", __func__, bus->nr); - bus->i2c_adap = cx25821_i2c_adap_template; - bus->i2c_client = cx25821_i2c_client_template; + 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.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); diff --git a/trunk/drivers/media/video/cx25821/cx25821-medusa-video.c b/trunk/drivers/media/video/cx25821/cx25821-medusa-video.c index 6a92e5c70c2a..313fb20a0b47 100644 --- a/trunk/drivers/media/video/cx25821/cx25821-medusa-video.c +++ b/trunk/drivers/media/video/cx25821/cx25821-medusa-video.c @@ -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; } diff --git a/trunk/drivers/media/video/cx25821/cx25821.h b/trunk/drivers/media/video/cx25821/cx25821.h index 8a9c0c869412..029f2934a6d8 100644 --- a/trunk/drivers/media/video/cx25821/cx25821.h +++ b/trunk/drivers/media/video/cx25821/cx25821.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -212,6 +213,7 @@ 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; diff --git a/trunk/drivers/media/video/davinci/Kconfig b/trunk/drivers/media/video/davinci/Kconfig index 52c5ca68cb3d..9337b5605c90 100644 --- a/trunk/drivers/media/video/davinci/Kconfig +++ b/trunk/drivers/media/video/davinci/Kconfig @@ -1,34 +1,30 @@ -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 +config DISPLAY_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Display" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG select VIDEO_DAVINCI_VPIF - select VIDEO_ADV7343 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_THS7303 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_ADV7343 + select VIDEO_THS7303 help - Enables Davinci VPIF module used for display devices. - This module is common for following DM6467/DA850/OMAPL138 - based display devices. + Support for DM6467 based display device. To compile this driver as a module, choose M here: the module will be called vpif_display. -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 +config CAPTURE_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Capture" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG select VIDEO_DAVINCI_VPIF help - Enables Davinci VPIF module used for captur devices. - This module is common for following DM6467/DA850/OMAPL138 - based capture devices. + Support for DM6467 based capture device. 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 VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE + depends on DISPLAY_DAVINCI_DM646X_EVM help Support for DaVinci VPIF Driver. diff --git a/trunk/drivers/media/video/davinci/Makefile b/trunk/drivers/media/video/davinci/Makefile index 74ed92d09257..ae7dafb689ab 100644 --- a/trunk/drivers/media/video/davinci/Makefile +++ b/trunk/drivers/media/video/davinci/Makefile @@ -5,10 +5,10 @@ # VPIF obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.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 +#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 # Capture: DM6446 and DM355 obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o diff --git a/trunk/drivers/media/video/davinci/vpbe_display.c b/trunk/drivers/media/video/davinci/vpbe_display.c index 6fe7034bea7c..e106b72810a9 100644 --- a/trunk/drivers/media/video/davinci/vpbe_display.c +++ b/trunk/drivers/media/video/davinci/vpbe_display.c @@ -1083,7 +1083,7 @@ vpbe_display_s_dv_preset(struct file *file, void *priv, } /* Set the given standard in the encoder */ - if (!vpbe_dev->ops.s_dv_preset) + if (NULL != vpbe_dev->ops.s_dv_preset) return -EINVAL; ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset); @@ -1517,8 +1517,6 @@ static int vpbe_display_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { struct v4l2_dbg_match *match = ®->match; - struct vpbe_fh *fh = file->private_data; - struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; if (match->type >= 2) { v4l2_subdev_call(vpbe_dev->venc, diff --git a/trunk/drivers/media/video/davinci/vpif.c b/trunk/drivers/media/video/davinci/vpif.c index b3637aff8fee..af9680273ff9 100644 --- a/trunk/drivers/media/video/davinci/vpif.c +++ b/trunk/drivers/media/video/davinci/vpif.c @@ -1,5 +1,5 @@ /* - * vpif - Video Port Interface driver + * vpif - DM646x 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,8 +23,6 @@ #include #include #include -#include -#include #include #include "vpif.h" @@ -42,7 +40,6 @@ static struct resource *res; spinlock_t vpif_lock; void __iomem *vpif_base; -struct clk *vpif_clk; /** * ch_params: video standard configuration parameters for vpif @@ -349,7 +346,7 @@ static void config_vpif_params(struct vpif_params *vpifparams, value = regr(reg); /* Set data width */ - value &= ~(0x3u << + value &= ((~(unsigned int)(0x3)) << VPIF_CH_DATA_WIDTH_BIT); value |= ((vpifparams->params.data_sz) << VPIF_CH_DATA_WIDTH_BIT); @@ -437,19 +434,10 @@ 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; @@ -457,44 +445,15 @@ static int __init vpif_probe(struct platform_device *pdev) 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, diff --git a/trunk/drivers/media/video/davinci/vpif.h b/trunk/drivers/media/video/davinci/vpif.h index c2ce4d97c279..8bcac65f9294 100644 --- a/trunk/drivers/media/video/davinci/vpif.h +++ b/trunk/drivers/media/video/davinci/vpif.h @@ -211,12 +211,6 @@ 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)) @@ -521,30 +515,6 @@ 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, @@ -599,21 +569,6 @@ 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 */ diff --git a/trunk/drivers/media/video/davinci/vpif_capture.c b/trunk/drivers/media/video/davinci/vpif_capture.c index 266025e5d81d..96046957bf21 100644 --- a/trunk/drivers/media/video/davinci/vpif_capture.c +++ b/trunk/drivers/media/video/davinci/vpif_capture.c @@ -80,45 +80,108 @@ static struct vpif_config_params config_params = { /* global variables */ 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 : translate user/virtual address to phy address + * @virtp: user/virtual address + * + * This inline function is used to convert user space virtual address to + * physical address. + */ +static inline u32 vpif_uservirt_to_phys(u32 virtp) +{ + unsigned long physp = 0; + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + + vma = find_vma(mm, virtp); + + /* For kernel direct-mapped memory, take the easy way */ + if (virtp >= PAGE_OFFSET) + physp = virt_to_phys((void *)virtp); + else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) + /** + * this will catch, kernel-allocated, mmaped-to-usermode + * addresses + */ + physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + else { + /* otherwise, use get_user_pages() for general userland pages */ + int res, nr_pages = 1; + struct page *pages; + + down_read(¤t->mm->mmap_sem); + + res = get_user_pages(current, current->mm, + virtp, nr_pages, 1, 0, &pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (res == nr_pages) + physp = __pa(page_address(&pages[0]) + + (virtp & ~PAGE_MASK)); + else { + vpif_err("get_user_pages failed\n"); + return 0; + } + } + return physp; +} /** * buffer_prepare : callback function for buffer prepare - * @vb: ptr to vb2_buffer + * @q : buffer queue ptr + * @vb: ptr to video buffer + * @field: field info * - * This is the callback function for buffer prepare when vb2_qbuf() + * This is the callback function for buffer prepare when videobuf_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 vb2_buffer *vb) +static int vpif_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) { /* Get the file handle object and channel object */ - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_queue *q = vb->vb2_queue; + struct vpif_fh *fh = q->priv_data; 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 (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 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 (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: @@ -127,79 +190,49 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) } /** - * 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 + * vpif_buffer_setup : Callback function for buffer setup. + * @q: buffer queue ptr + * @count: number of buffers + * @size: size of the buffer * * This callback function is called when reqbuf() is called to adjust * the buffer count and buffer 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[]) +static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) { /* Get the file handle object and channel object */ - struct vpif_fh *fh = vb2_get_drv_priv(vq); + struct vpif_fh *fh = q->priv_data; 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) { - /* 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)--; - } - - } else { - size = common->fmt.fmt.pix.sizeimage; - } - - if (*nbuffers < config_params.min_numbuffers) - *nbuffers = config_params.min_numbuffers; + if (V4L2_MEMORY_MMAP != common->memory) + return 0; - *nplanes = 1; - sizes[0] = size; - alloc_ctxs[0] = common->alloc_ctx; + /* Calculate the size of the buffer */ + *size = config_params.channel_bufsize[ch->channel_id]; + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; return 0; } /** * vpif_buffer_queue : Callback function to add buffer to DMA queue - * @vb: ptr to vb2_buffer + * @q: ptr to videobuf_queue + * @vb: ptr to videobuf_buffer */ -static void vpif_buffer_queue(struct vb2_buffer *vb) +static void vpif_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) { /* Get the file handle object and channel object */ - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpif_fh *fh = q->priv_data; 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]; @@ -207,189 +240,43 @@ static void vpif_buffer_queue(struct vb2_buffer *vb) vpif_dbg(2, debug, "vpif_buffer_queue\n"); /* add the buffer to the DMA queue */ - list_add_tail(&buf->list, &common->dma_queue); + list_add_tail(&vb->queue, &common->dma_queue); + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; } /** - * vpif_buf_cleanup : Callback function to free buffer - * @vb: ptr to vb2_buffer + * vpif_buffer_release : Callback function to free buffer + * @q: buffer queue ptr + * @vb: ptr to video buffer * - * This function is called from the videobuf2 layer to free memory + * This function is called from the videobuf layer to free memory * allocated to the buffers */ -static void vpif_buf_cleanup(struct vb2_buffer *vb) +static void vpif_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) { /* Get the file handle object and channel object */ - 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 vpif_fh *fh = q->priv_data; 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); - + videobuf_dma_contig_free(q, vb); + vb->state = VIDEOBUF_NEEDS_INIT; } -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_cap_buffer *buf = container_of(vb, - struct vpif_cap_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_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 @@ -400,9 +287,9 @@ static struct vb2_ops video_qops = { */ static void vpif_process_buffer_complete(struct common_obj *common) { - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); - vb2_buffer_done(&common->cur_frm->vb, - VB2_BUF_STATE_DONE); + do_gettimeofday(&common->cur_frm->ts); + common->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&common->cur_frm->done); /* Make curFrm pointing to nextFrm */ common->cur_frm = common->next_frm; } @@ -420,11 +307,14 @@ static void vpif_schedule_next_buffer(struct common_obj *common) unsigned long addr = 0; common->next_frm = list_entry(common->dma_queue.next, - struct vpif_cap_buffer, list); + struct videobuf_buffer, queue); /* Remove that buffer from the buffer queue */ - 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); + 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); /* Set top and bottom field addresses in VPIF registers */ common->set_addr(addr + common->ytop_off, @@ -451,9 +341,6 @@ 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; @@ -598,7 +485,10 @@ static void vpif_calculate_offsets(struct channel_obj *ch) } else vid_ch->buf_field = common->fmt.fmt.pix.field; - sizeimage = common->fmt.fmt.pix.sizeimage; + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = common->fmt.fmt.pix.sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; hpitch = common->fmt.fmt.pix.bytesperline; vpitch = sizeimage / (hpitch * 2); @@ -750,7 +640,10 @@ static int vpif_check_format(struct channel_obj *ch, hpitch = vpif_params->std_info.width; } - sizeimage = pixfmt->sizeimage; + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = pixfmt->sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; vpitch = sizeimage / (hpitch * 2); @@ -810,7 +703,7 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode) } /** - * vpif_mmap : It is used to map kernel space buffers into user spaces + * vpfe_mmap : It is used to map kernel space buffers into user spaces * @filep: file pointer * @vma: ptr to vm_area_struct */ @@ -823,7 +716,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) vpif_dbg(2, debug, "vpif_mmap\n"); - return vb2_mmap(&common->buffer_queue, vma); + return videobuf_mmap_mapper(&common->buffer_queue, vma); } /** @@ -840,7 +733,7 @@ static unsigned int vpif_poll(struct file *filep, poll_table * wait) vpif_dbg(2, debug, "vpif_poll\n"); if (common->started) - return vb2_poll(&common->buffer_queue, filep, wait); + return videobuf_poll_stream(filep, &common->buffer_queue, wait); return 0; } @@ -919,7 +812,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 vpif file + * This function deletes buffer queue, frees the buffers and the vpfe file * handle */ static int vpif_release(struct file *filep) @@ -948,8 +841,8 @@ static int vpif_release(struct file *filep) } common->started = 0; /* Free buffers allocated */ - vb2_queue_release(&common->buffer_queue); - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); + videobuf_queue_cancel(&common->buffer_queue); + videobuf_mmap_free(&common->buffer_queue); } /* Decrement channel usrs counter */ @@ -979,7 +872,6 @@ 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"); @@ -995,7 +887,7 @@ static int vpif_reqbufs(struct file *file, void *priv, } } - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev) + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type) return -EINVAL; index = VPIF_VIDEO_INDEX; @@ -1005,21 +897,14 @@ static int vpif_reqbufs(struct file *file, void *priv, if (0 != common->io_usrs) return -EBUSY; - /* 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); + /* 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); /* Set io allowed member of file handle to TRUE */ fh->io_allowed[index] = 1; @@ -1030,7 +915,7 @@ static int vpif_reqbufs(struct file *file, void *priv, INIT_LIST_HEAD(&common->dma_queue); /* Allocate buffers */ - return vb2_reqbufs(&common->buffer_queue, reqbuf); + return videobuf_reqbufs(&common->buffer_queue, reqbuf); } /** @@ -1056,7 +941,7 @@ static int vpif_querybuf(struct file *file, void *priv, return -EINVAL; } - return vb2_querybuf(&common->buffer_queue, buf); + return videobuf_querybuf(&common->buffer_queue, buf); } /** @@ -1072,6 +957,10 @@ 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"); @@ -1081,11 +970,76 @@ 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; } - return vb2_qbuf(&common->buffer_queue, buf); + 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; } /** @@ -1102,8 +1056,8 @@ static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) vpif_dbg(2, debug, "vpif_dqbuf\n"); - return vb2_dqbuf(&common->buffer_queue, buf, - (file->f_flags & O_NONBLOCK)); + return videobuf_dqbuf(&common->buffer_queue, buf, + file->f_flags & O_NONBLOCK); } /** @@ -1116,11 +1070,13 @@ 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"); @@ -1166,13 +1122,95 @@ static int vpif_streamon(struct file *file, void *priv, return ret; } - /* Call vb2_streamon to start streaming in videobuf2 */ - ret = vb2_streamon(&common->buffer_queue, buftype); + /* Call videobuf_streamon to start streaming in videobuf */ + ret = videobuf_streamon(&common->buffer_queue); if (ret) { - vpif_dbg(1, debug, "vb2_streamon\n"); + vpif_dbg(1, debug, "videobuf_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; } @@ -1227,7 +1265,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 vb2_streamoff(&common->buffer_queue, buftype); + return videobuf_streamoff(&common->buffer_queue); } /** @@ -1641,7 +1679,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, "VPIF Platform", sizeof(cap->bus_info)); + strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info)); strlcpy(cap->card, config->card_name, sizeof(cap->card)); return 0; @@ -2130,7 +2168,6 @@ 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; @@ -2149,8 +2186,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_SHARED, - "VPIF_Capture", + if (request_irq(i, vpif_channel_isr, IRQF_DISABLED, + "DM646x_Capture", (void *)(&vpif_obj.dev[k]->channel_id))) { err = -EBUSY; i--; @@ -2179,29 +2216,12 @@ 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), - "VPIF_Capture_DRIVER_V%s", + "DM646x_VPIFCapture_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; @@ -2255,7 +2275,8 @@ static __init int vpif_probe(struct platform_device *pdev) vpif_obj.sd[i]->grp_id = 1 << i; } - v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n"); + v4l2_info(&vpif_obj.v4l2_dev, + "DM646x VPIF capture driver initialized\n"); return 0; probe_subdev_out: @@ -2312,70 +2333,26 @@ 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) { - - 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; + return -1; } -/* +/** * vpif_resume: vpif device suspend + * + * TODO: Add resume code here */ -static int vpif_resume(struct device *dev) +static int +vpif_resume(struct device *dev) { - 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; + return -1; } static const struct dev_pm_ops vpif_dev_pm_ops = { @@ -2383,16 +2360,11 @@ 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_pm_ops, + .pm = &vpif_dev_pm_ops, }, .probe = vpif_probe, .remove = vpif_remove, diff --git a/trunk/drivers/media/video/davinci/vpif_capture.h b/trunk/drivers/media/video/davinci/vpif_capture.h index 3511510f43ee..a693d4ebda55 100644 --- a/trunk/drivers/media/video/davinci/vpif_capture.h +++ b/trunk/drivers/media/video/davinci/vpif_capture.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include "vpif.h" @@ -60,16 +60,11 @@ 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 vpif_cap_buffer *cur_frm; + struct videobuf_buffer *cur_frm; /* Pointer pointing to current v4l2_buffer */ - struct vpif_cap_buffer *next_frm; + struct videobuf_buffer *next_frm; /* * This field keeps track of type of buffer exchange mechanism * user has selected @@ -78,9 +73,7 @@ struct common_obj { /* Used to store pixel format */ struct v4l2_format fmt; /* Buffer queue used in video-buf */ - struct vb2_queue buffer_queue; - /* allocator-specific contexts for each plane */ - struct vb2_alloc_ctx *alloc_ctx; + struct videobuf_queue buffer_queue; /* Queue of filled frames */ struct list_head dma_queue; /* Used in video-buf */ @@ -158,7 +151,6 @@ 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 */ diff --git a/trunk/drivers/media/video/davinci/vpif_display.c b/trunk/drivers/media/video/davinci/vpif_display.c index e129c98921ad..e6488ee7db18 100644 --- a/trunk/drivers/media/video/davinci/vpif_display.c +++ b/trunk/drivers/media/video/davinci/vpif_display.c @@ -46,7 +46,7 @@ MODULE_DESCRIPTION("TI DaVinci VPIF Display driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(VPIF_DISPLAY_VERSION); -#define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50) +#define DM646X_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,38 +82,89 @@ 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); /* - * buffer_prepare: This is the callback function called from vb2_qbuf() + * vpif_uservirt_to_phys: This function is used to convert user + * space virtual address to physical address. + */ +static u32 vpif_uservirt_to_phys(u32 virtp) +{ + struct mm_struct *mm = current->mm; + unsigned long physp = 0; + struct vm_area_struct *vma; + + vma = find_vma(mm, virtp); + + /* For kernel direct-mapped memory, take the easy way */ + if (virtp >= PAGE_OFFSET) { + physp = virt_to_phys((void *)virtp); + } else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) { + /* this will catch, kernel-allocated, mmaped-to-usermode addr */ + physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + } else { + /* otherwise, use get_user_pages() for general userland pages */ + int res, nr_pages = 1; + struct page *pages; + down_read(¤t->mm->mmap_sem); + + res = get_user_pages(current, current->mm, + virtp, nr_pages, 1, 0, &pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (res == nr_pages) { + physp = __pa(page_address(&pages[0]) + + (virtp & ~PAGE_MASK)); + } else { + vpif_err("get_user_pages failed\n"); + return 0; + } + } + + return physp; +} + +/* + * buffer_prepare: This is the callback function called from videobuf_qbuf() * function the buffer is prepared and user space virtual address is converted * into physical address */ -static int vpif_buffer_prepare(struct vb2_buffer *vb) +static int vpif_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) { - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_queue *q = vb->vb2_queue; + struct vpif_fh *fh = q->priv_data; struct common_obj *common; unsigned long addr; common = &fh->channel->common[VPIF_VIDEO_INDEX]; - 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)) + 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 (!vb->baddr) { + vpif_err("buffer_address is 0\n"); + return -EINVAL; + } + + vb->boff = vpif_uservirt_to_phys(vb->baddr); + if (!ISALIGNED(vb->boff)) goto buf_align_exit; + } - 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; - } + 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; @@ -123,240 +174,71 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) } /* - * vpif_buffer_queue_setup: This function allocates memory for the buffers + * vpif_buffer_setup: This function allocates memory for the buffers */ -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[]) +static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) { - struct vpif_fh *fh = vb2_get_drv_priv(vq); + struct vpif_fh *fh = q->priv_data; struct channel_obj *ch = fh->channel; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - unsigned long size; - - 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; - } - if (*nbuffers < config_params.min_numbuffers) - *nbuffers = config_params.min_numbuffers; + if (V4L2_MEMORY_MMAP != common->memory) + return 0; + + *size = config_params.channel_bufsize[ch->channel_id]; + if (*count < config_params.min_numbuffers) + *count = 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 vb2_buffer *vb) +static void vpif_buffer_queue(struct videobuf_queue *q, + struct videobuf_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 vpif_fh *fh = q->priv_data; struct common_obj *common; - common = &ch->common[VPIF_VIDEO_INDEX]; + common = &fh->channel->common[VPIF_VIDEO_INDEX]; /* add the buffer to the DMA queue */ - list_add_tail(&buf->list, &common->dma_queue); + list_add_tail(&vb->queue, &common->dma_queue); + vb->state = VIDEOBUF_QUEUED; } /* - * vpif_buf_cleanup: This function is called from the videobuf2 layer to + * vpif_buffer_release: This function is called from the videobuf 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) +static void vpif_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) { - struct vpif_fh *fh = vb2_get_drv_priv(vq); + struct vpif_fh *fh = q->priv_data; struct channel_obj *ch = fh->channel; struct common_obj *common; + unsigned int buf_size = 0; 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 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; + videobuf_dma_contig_free(q, vb); + vb->state = VIDEOBUF_NEEDS_INIT; - 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; + if (V4L2_MEMORY_MMAP != common->memory) + return; - return 0; + buf_size = config_params.channel_bufsize[ch->channel_id]; } -/* 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 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 void process_progressive_mode(struct common_obj *common) { @@ -364,14 +246,14 @@ static void process_progressive_mode(struct common_obj *common) /* Get the next buffer from buffer queue */ common->next_frm = list_entry(common->dma_queue.next, - struct vpif_disp_buffer, list); + struct videobuf_buffer, queue); /* Remove that buffer from the buffer queue */ - list_del(&common->next_frm->list); + list_del(&common->next_frm->queue); /* Mark status of the buffer as active */ - common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + common->next_frm->state = VIDEOBUF_ACTIVE; /* Set top and bottom field addrs in VPIF registers */ - addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0); + addr = videobuf_to_dma_contig(common->next_frm); common->set_addr(addr + common->ytop_off, addr + common->ybtm_off, addr + common->ctop_off, @@ -389,10 +271,11 @@ static void process_interlaced_mode(int fid, struct common_obj *common) /* one frame is displayed If next frame is * available, release cur_frm and move on */ /* Copy frame display time */ - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + do_gettimeofday(&common->cur_frm->ts); /* Change status of the cur_frm */ - vb2_buffer_done(&common->cur_frm->vb, - VB2_BUF_STATE_DONE); + common->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&common->cur_frm->done); /* Make cur_frm pointing to next_frm */ common->cur_frm = common->next_frm; @@ -424,9 +307,6 @@ 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++) { @@ -443,10 +323,9 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) if (!channel_first_int[i][channel_id]) { /* Mark status of the cur_frm to * done and unlock semaphore on it */ - do_gettimeofday(&common->cur_frm->vb. - v4l2_buf.timestamp); - vb2_buffer_done(&common->cur_frm->vb, - VB2_BUF_STATE_DONE); + do_gettimeofday(&common->cur_frm->ts); + common->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&common->cur_frm->done); /* Make cur_frm pointing to next_frm */ common->cur_frm = common->next_frm; } @@ -564,7 +443,10 @@ static void vpif_calculate_offsets(struct channel_obj *ch) vid_ch->buf_field = common->fmt.fmt.pix.field; } - sizeimage = common->fmt.fmt.pix.sizeimage; + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = common->fmt.fmt.pix.sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; hpitch = common->fmt.fmt.pix.bytesperline; vpitch = sizeimage / (hpitch * 2); @@ -641,7 +523,10 @@ static int vpif_check_format(struct channel_obj *ch, if (pixfmt->bytesperline <= 0) goto invalid_pitch_exit; - sizeimage = pixfmt->sizeimage; + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = pixfmt->sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; if (vpif_update_resolution(ch)) return -EINVAL; @@ -698,7 +583,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) vpif_dbg(2, debug, "vpif_mmap\n"); - return vb2_mmap(&common->buffer_queue, vma); + return videobuf_mmap_mapper(&common->buffer_queue, vma); } /* @@ -711,7 +596,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 vb2_poll(&common->buffer_queue, filep, wait); + return videobuf_poll_stream(filep, &common->buffer_queue, wait); return 0; } @@ -780,11 +665,9 @@ static int vpif_release(struct file *filep) channel3_intr_enable(0); } common->started = 0; - /* Free buffers allocated */ - vb2_queue_release(&common->buffer_queue); - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); - + videobuf_queue_cancel(&common->buffer_queue); + videobuf_mmap_free(&common->buffer_queue); common->numbuffers = config_params.numbuffers[ch->channel_id]; } @@ -923,7 +806,6 @@ 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, @@ -943,8 +825,9 @@ static int vpif_reqbufs(struct file *file, void *priv, common = &ch->common[index]; - if (common->fmt.type != reqbuf->type || !vpif_dev) + if (common->fmt.type != reqbuf->type) return -EINVAL; + if (0 != common->io_usrs) return -EBUSY; @@ -956,21 +839,14 @@ 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); - vb2_queue_init(q); + /* 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); /* Set io allowed member of file handle to TRUE */ fh->io_allowed[index] = 1; @@ -979,8 +855,9 @@ 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 vb2_reqbufs(&common->buffer_queue, reqbuf); + return videobuf_reqbufs(&common->buffer_queue, reqbuf); } static int vpif_querybuf(struct file *file, void *priv, @@ -993,25 +870,22 @@ static int vpif_querybuf(struct file *file, void *priv, if (common->fmt.type != tbuf->type) return -EINVAL; - return vb2_querybuf(&common->buffer_queue, tbuf); + return videobuf_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; - - if (!buf || !priv) - return -EINVAL; - fh = priv; - ch = fh->channel; - if (!ch) - return -EINVAL; + 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; - common = &(ch->common[VPIF_VIDEO_INDEX]); - if (common->fmt.type != buf->type) + if (common->fmt.type != tbuf.type) return -EINVAL; if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { @@ -1019,7 +893,73 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return -EACCES; } - return vb2_qbuf(&common->buffer_queue, buf); + 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; } static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) @@ -1029,7 +969,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 & VPIF_V4L2_STD)) + if (!(*std_id & DM646X_V4L2_STD)) return -EINVAL; if (common->started) { @@ -1086,7 +1026,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 vb2_dqbuf(&common->buffer_queue, p, + return videobuf_dqbuf(&common->buffer_queue, p, (file->f_flags & O_NONBLOCK)); } @@ -1097,6 +1037,10 @@ 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) { @@ -1128,13 +1072,82 @@ static int vpif_streamon(struct file *file, void *priv, if (ret < 0) return ret; - /* Call vb2_streamon to start streaming in videobuf2 */ - ret = vb2_streamon(&common->buffer_queue, buftype); + /* Call videobuf_streamon to start streaming in videobuf */ + ret = videobuf_streamon(&common->buffer_queue); if (ret < 0) { - vpif_err("vb2_streamon\n"); + vpif_err("videobuf_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; } @@ -1144,8 +1157,6 @@ 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"); @@ -1165,22 +1176,18 @@ 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 vb2_streamoff(&common->buffer_queue, buftype); + return videobuf_streamoff(&common->buffer_queue); } static int vpif_cropcap(struct file *file, void *priv, @@ -1213,7 +1220,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 = VPIF_V4L2_STD; + output->std = DM646X_V4L2_STD; return 0; } @@ -1598,7 +1605,7 @@ static struct video_device vpif_video_template = { .name = "vpif", .fops = &vpif_fops, .ioctl_ops = &vpif_ioctl_ops, - .tvnorms = VPIF_V4L2_STD, + .tvnorms = DM646X_V4L2_STD, .current_norm = V4L2_STD_625_50, }; @@ -1680,9 +1687,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) { @@ -1699,8 +1706,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_SHARED, - "VPIF_Display", + if (request_irq(i, vpif_channel_isr, IRQF_DISABLED, + "DM646x_Display", (void *)(&vpif_obj.dev[k]->channel_id))) { err = -EBUSY; goto vpif_int_err; @@ -1730,31 +1737,13 @@ 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), - "VPIF_Display_DRIVER_V%s", + "DM646x_VPIFDisplay_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 */ @@ -1834,7 +1823,7 @@ static __init int vpif_probe(struct platform_device *pdev) } v4l2_info(&vpif_obj.v4l2_dev, - " VPIF display driver initialized\n"); + "DM646x VPIF display driver initialized\n"); return 0; probe_subdev_out: @@ -1882,81 +1871,10 @@ 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, diff --git a/trunk/drivers/media/video/davinci/vpif_display.h b/trunk/drivers/media/video/davinci/vpif_display.h index 8967ffb44058..56879d1a0684 100644 --- a/trunk/drivers/media/video/davinci/vpif_display.h +++ b/trunk/drivers/media/video/davinci/vpif_display.h @@ -1,5 +1,5 @@ /* - * VPIF display header file + * DM646x display header file * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ * @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include "vpif.h" @@ -73,29 +73,21 @@ 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 vpif_disp_buffer *cur_frm; /* Pointer pointing to current - * vb2_buffer */ - struct vpif_disp_buffer *next_frm; /* Pointer pointing to next - * vb2_buffer */ + struct videobuf_buffer *cur_frm; /* Pointer pointing to current + * videobuf_buffer */ + struct videobuf_buffer *next_frm; /* Pointer pointing to next + * videobuf_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 vb2_queue buffer_queue; /* Buffer queue used in + struct videobuf_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 */ @@ -166,7 +158,6 @@ 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; }; diff --git a/trunk/drivers/media/video/gspca/benq.c b/trunk/drivers/media/video/gspca/benq.c index 352f32190e68..9769f17915c0 100644 --- a/trunk/drivers/media/video/gspca/benq.c +++ b/trunk/drivers/media/video/gspca/benq.c @@ -33,6 +33,10 @@ 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, @@ -252,6 +256,8 @@ 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, @@ -282,7 +288,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/conex.c b/trunk/drivers/media/video/gspca/conex.c index c9052f20435e..f39fee0fd10f 100644 --- a/trunk/drivers/media/video/gspca/conex.c +++ b/trunk/drivers/media/video/gspca/conex.c @@ -31,18 +31,74 @@ MODULE_AUTHOR("Michel Xhaard "); 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 */ - struct v4l2_ctrl *brightness; - struct v4l2_ctrl *contrast; - struct v4l2_ctrl *sat; + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + u8 quality; +#define QUALITY_MIN 30 +#define QUALITY_MAX 60 +#define QUALITY_DEF 40 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, @@ -761,11 +817,17 @@ 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; } @@ -787,7 +849,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, QUALITY); + jpeg_set_qual(sd->jpeg_hdr, sd->quality); cx11646_initsize(gspca_dev); cx11646_fw(gspca_dev); @@ -841,99 +903,142 @@ 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, s32 val, s32 sat) +static void setbrightness(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; __u8 reg51c[2]; + __u8 bright; + __u8 colors; - regE5cbx[2] = val; + bright = sd->brightness; + regE5cbx[2] = bright; 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] = sat; + reg51c[1] = colors; 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, s32 val, s32 sat) +static void setcontrast(struct gspca_dev *gspca_dev) { + 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] = val; + regE5acx[2] = sd->contrast; reg_w(gspca_dev, 0x00e5, regE5acx, 4); reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ reg51c[0] = 0x77; - reg51c[1] = sat; + reg51c[1] = sd->colors; reg_w(gspca_dev, 0x0051, reg51c, 2); reg_w(gspca_dev, 0x0010, reg10, 2); reg_w_val(gspca_dev, 0x0070, reg70); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; + struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_err = 0; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) 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 gspca_dev->usb_err; + *val = sd->brightness; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +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; -static int sd_init_controls(struct gspca_dev *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; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - 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; + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) { + setbrightness(gspca_dev); + setcontrast(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; + 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 -- */ @@ -959,7 +1064,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/cpia1.c b/trunk/drivers/media/video/gspca/cpia1.c index 2499a881d9a3..8f33bbd091ad 100644 --- a/trunk/drivers/media/video/gspca/cpia1.c +++ b/trunk/drivers/media/video/gspca/cpia1.c @@ -225,15 +225,6 @@ 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] = @@ -369,9 +360,135 @@ 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[] = { @@ -653,6 +770,15 @@ 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; @@ -672,15 +798,6 @@ 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 */ @@ -993,6 +1110,9 @@ 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; @@ -1431,10 +1551,8 @@ 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)", @@ -1444,25 +1562,8 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = mode; cam->nmodes = ARRAY_SIZE(mode); - 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; - } + sd_setfreq(gspca_dev, FREQ_DEF); - /* 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; } @@ -1501,6 +1602,21 @@ 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) */ @@ -1669,6 +1785,9 @@ 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); @@ -1752,123 +1871,235 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev) do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; + struct sd *sd = (struct sd *) gspca_dev; + int ret; - gspca_dev->usb_err = 0; + 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; +} - if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY) - return 0; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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); + *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; break; - case V4L2_CID_CONTRAST: - sd->params.colourParams.contrast = ctrl->val; - gspca_dev->usb_err = command_setcolourparams(gspca_dev); + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + on = 1; + sd->mainsFreq = 0; break; - case V4L2_CID_SATURATION: - sd->params.colourParams.saturation = ctrl->val; - gspca_dev->usb_err = command_setcolourparams(gspca_dev); + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + on = 1; + sd->mainsFreq = 1; break; - case V4L2_CID_POWER_LINE_FREQUENCY: - 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); + 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 V4L2_CID_ILLUMINATORS_1: - sd->params.qx3.bottomlight = ctrl->val; - gspca_dev->usb_err = command_setlights(gspca_dev); + case 2: + sd->params.qx3.toplight = val ? 1 : 0; break; - case V4L2_CID_ILLUMINATORS_2: - sd->params.qx3.toplight = ctrl->val; - gspca_dev->usb_err = command_setlights(gspca_dev); + 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 CPIA1_CID_COMP_TARGET: - sd->params.compressionTarget.frTargeting = ctrl->val; - gspca_dev->usb_err = command_setcompressiontarget(gspca_dev); + case 2: + *val = sd->params.qx3.toplight; break; + default: + return -EINVAL; } - return gspca_dev->usb_err; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val) +{ + return sd_getilluminator(gspca_dev, val, 1); +} -static int sd_init_controls(struct gspca_dev *gspca_dev) +static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val) { - 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); + return sd_getilluminator(gspca_dev, val, 2); +} - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; +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; + } + 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; + } + break; } - return 0; + return -EINVAL; } /* 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 @@ -1898,7 +2129,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/etoms.c b/trunk/drivers/media/video/gspca/etoms.c index 38f68e11c3a2..81a4adbd9f7c 100644 --- a/trunk/drivers/media/video/gspca/etoms.c +++ b/trunk/drivers/media/video/gspca/etoms.c @@ -32,6 +32,9 @@ 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; @@ -41,6 +44,76 @@ 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, @@ -391,31 +464,36 @@ 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, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { + 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, val); + reg_w_val(gspca_dev, ET_O_RED + i, brightness); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + __u8 contrast = sd->contrast; - memset(RGBG, val, sizeof(RGBG) - 2); + memset(RGBG, contrast, sizeof(RGBG) - 2); reg_w(gspca_dev, ET_G_RED, RGBG, 6); } -static void setcolors(struct gspca_dev *gspca_dev, s32 val) +static void setcolors(struct gspca_dev *gspca_dev) { 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] = val; /* red */ - I2cc[0] = 15 - val; /* blue */ + I2cc[3] = colors; /* red */ + I2cc[0] = 15 - colors; /* blue */ /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */ /* I2cc[1] = I2cc[2] = green; */ if (sd->sensor == SENSOR_PAS106) { @@ -426,16 +504,15 @@ static void setcolors(struct gspca_dev *gspca_dev, s32 val) I2cc[3], I2cc[0], green); */ } -static s32 getcolors(struct gspca_dev *gspca_dev) +static void 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 */ - return gspca_dev->usb_buf[0] & 0x0f; + sd->colors = gspca_dev->usb_buf[0] & 0x0f; } - return 0; } static void setautogain(struct gspca_dev *gspca_dev) @@ -545,7 +622,8 @@ 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); - setcolors(gspca_dev, getcolors(gspca_dev)); + getcolors(gspca_dev); + setcolors(gspca_dev); } /* this function is called at probe time */ @@ -563,7 +641,12 @@ 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; } @@ -697,68 +780,85 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - 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_SATURATION: - setcolors(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - sd->autogain = ctrl->val; - setautogain(gspca_dev); - break; - } - return gspca_dev->usb_err; + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; -static int sd_init_controls(struct gspca_dev *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; - 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; - } + 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) + 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; } /* 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, @@ -792,7 +892,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/finepix.c b/trunk/drivers/media/video/gspca/finepix.c index c8f2201cc35a..6e26c93b4656 100644 --- a/trunk/drivers/media/video/gspca/finepix.c +++ b/trunk/drivers/media/video/gspca/finepix.c @@ -299,7 +299,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/gl860/gl860.c b/trunk/drivers/media/video/gspca/gl860/gl860.c index ced3b71f14e5..c549574c1c7e 100644 --- a/trunk/drivers/media/video/gspca/gl860/gl860.c +++ b/trunk/drivers/media/video/gspca/gl860/gl860.c @@ -521,7 +521,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/gspca.c b/trunk/drivers/media/video/gspca/gspca.c index d4e8343f5b10..31721eadc597 100644 --- a/trunk/drivers/media/video/gspca/gspca.c +++ b/trunk/drivers/media/video/gspca/gspca.c @@ -930,7 +930,6 @@ 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) @@ -1050,6 +1049,12 @@ 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); } @@ -1059,6 +1064,12 @@ 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); } @@ -1069,6 +1080,9 @@ 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); } @@ -1122,10 +1136,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, int mode; mode = gspca_dev->curr_mode; - 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; + memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode], + sizeof fmt->fmt.pix); return 0; } @@ -1156,10 +1168,8 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, /* else ; * no chance, return this mode */ } - 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; + memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode], + sizeof fmt->fmt.pix); return mode; /* used when s_fmt */ } @@ -1274,6 +1284,9 @@ 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); @@ -1681,6 +1694,8 @@ 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); } @@ -1690,6 +1705,8 @@ 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); } @@ -2273,20 +2290,6 @@ 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, @@ -2426,6 +2429,7 @@ 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); diff --git a/trunk/drivers/media/video/gspca/jeilinj.c b/trunk/drivers/media/video/gspca/jeilinj.c index 26b99310d628..5ab3f7e12760 100644 --- a/trunk/drivers/media/video/gspca/jeilinj.c +++ b/trunk/drivers/media/video/gspca/jeilinj.c @@ -54,13 +54,21 @@ 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 */ @@ -131,21 +139,23 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) } } -static void setfreq(struct gspca_dev *gspca_dev, s32 val) +static void setfreq(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 freq_commands[][2] = { {0x71, 0x80}, {0x70, 0x07} }; - freq_commands[0][1] |= val >> 1; + freq_commands[0][1] |= (sd->ctrls[LIGHTFREQ].val >> 1); jlj_write2(gspca_dev, freq_commands[0]); jlj_write2(gspca_dev, freq_commands[1]); } -static void setcamquality(struct gspca_dev *gspca_dev, s32 val) +static void setcamquality(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 quality_commands[][2] = { {0x71, 0x1E}, {0x70, 0x06} @@ -153,7 +163,7 @@ static void setcamquality(struct gspca_dev *gspca_dev, s32 val) u8 camquality; /* adapt camera quality from jpeg quality */ - camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX) + camquality = ((QUALITY_MAX - sd->quality) * CAMQUALITY_MAX) / (QUALITY_MAX - QUALITY_MIN); quality_commands[0][1] += camquality; @@ -161,58 +171,130 @@ static void setcamquality(struct gspca_dev *gspca_dev, s32 val) jlj_write2(gspca_dev, quality_commands[1]); } -static void setautogain(struct gspca_dev *gspca_dev, s32 val) +static void setautogain(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 autogain_commands[][2] = { {0x94, 0x02}, {0xcf, 0x00} }; - autogain_commands[1][1] = val << 4; + autogain_commands[1][1] = (sd->ctrls[AUTOGAIN].val << 4); jlj_write2(gspca_dev, autogain_commands[0]); jlj_write2(gspca_dev, autogain_commands[1]); } -static void setred(struct gspca_dev *gspca_dev, s32 val) +static void setred(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 setred_commands[][2] = { {0x94, 0x02}, {0xe6, 0x00} }; - setred_commands[1][1] = val; + setred_commands[1][1] = sd->ctrls[RED].val; jlj_write2(gspca_dev, setred_commands[0]); jlj_write2(gspca_dev, setred_commands[1]); } -static void setgreen(struct gspca_dev *gspca_dev, s32 val) +static void setgreen(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 setgreen_commands[][2] = { {0x94, 0x02}, {0xe7, 0x00} }; - setgreen_commands[1][1] = val; + setgreen_commands[1][1] = sd->ctrls[GREEN].val; jlj_write2(gspca_dev, setgreen_commands[0]); jlj_write2(gspca_dev, setgreen_commands[1]); } -static void setblue(struct gspca_dev *gspca_dev, s32 val) +static void setblue(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 setblue_commands[][2] = { {0x94, 0x02}, {0xe9, 0x00} }; - setblue_commands[1][1] = val; + setblue_commands[1][1] = sd->ctrls[BLUE].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; @@ -262,9 +344,9 @@ static int jlj_start(struct gspca_dev *gspca_dev) if (start_commands[i].ack_wanted) jlj_read1(gspca_dev, response); } - setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); + setcamquality(gspca_dev); msleep(2); - setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); + setfreq(gspca_dev); if (gspca_dev->usb_err < 0) PDEBUG(D_ERR, "Start streaming command failed"); return gspca_dev->usb_err; @@ -321,6 +403,7 @@ 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; @@ -396,81 +479,25 @@ static const struct usb_device_id device_table[] = { MODULE_DEVICE_TABLE(usb, device_table); -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) { - 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) { + switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: - 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); + 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; + } 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; - 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; + return -EINVAL; } static int sd_set_jcomp(struct gspca_dev *gspca_dev, @@ -478,7 +505,16 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; - v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + 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); + } return 0; } @@ -488,7 +524,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 = v4l2_ctrl_g_ctrl(sd->jpegqual); + jcomp->quality = sd->quality; jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -510,10 +546,12 @@ 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, }; @@ -541,7 +579,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/jl2005bcd.c b/trunk/drivers/media/video/gspca/jl2005bcd.c index cf9d9fca5b84..9c591c7c6f54 100644 --- a/trunk/drivers/media/video/gspca/jl2005bcd.c +++ b/trunk/drivers/media/video/gspca/jl2005bcd.c @@ -505,6 +505,8 @@ 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, @@ -534,7 +536,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/kinect.c b/trunk/drivers/media/video/gspca/kinect.c index 40ad6687ee5d..e8e8f2fe9166 100644 --- a/trunk/drivers/media/video/gspca/kinect.c +++ b/trunk/drivers/media/video/gspca/kinect.c @@ -63,6 +63,12 @@ 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 @@ -367,12 +373,15 @@ 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, */ @@ -401,7 +410,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/konica.c b/trunk/drivers/media/video/gspca/konica.c index bbf91e07e38b..f0c0d74dfe92 100644 --- a/trunk/drivers/media/video/gspca/konica.c +++ b/trunk/drivers/media/video/gspca/konica.c @@ -50,8 +50,107 @@ 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 @@ -103,8 +202,7 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) 0, 1000); if (ret < 0) { - pr_err("reg_w err writing %02x to %02x: %d\n", - value, index, ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -125,7 +223,7 @@ static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index) 2, 1000); if (ret < 0) { - pr_err("reg_r err %d\n", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -144,33 +242,34 @@ 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) { - 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); - } + /* 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]); reg_w(gspca_dev, 0, 0x0d); - return gspca_dev->usb_err; + return 0; } static int sd_start(struct gspca_dev *gspca_dev) @@ -190,6 +289,12 @@ 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); @@ -374,82 +479,125 @@ static void sd_isoc_irq(struct urb *urb) pr_err("usb_submit_urb(status_urb) ret %d\n", st); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - 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; + struct sd *sd = (struct sd *) gspca_dev; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, BRIGHTNESS_REG); - konica_stream_on(gspca_dev); - break; - case V4L2_CID_CONTRAST: + sd->brightness = val; + if (gspca_dev->streaming) { konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, CONTRAST_REG); + reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG); konica_stream_on(gspca_dev); - break; - case V4L2_CID_SATURATION: + } + + 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) { konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, SATURATION_REG); + reg_w(gspca_dev, sd->contrast, CONTRAST_REG); konica_stream_on(gspca_dev); - break; - case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + } + + 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) { konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, WHITEBAL_REG); + reg_w(gspca_dev, sd->saturation, SATURATION_REG); konica_stream_on(gspca_dev); - break; - case V4L2_CID_SHARPNESS: + } + 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) { konica_stream_off(gspca_dev); - reg_w(gspca_dev, ctrl->val, SHARPNESS_REG); + reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG); konica_stream_on(gspca_dev); - break; } - return gspca_dev->usb_err; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->whitebal; -static int sd_init_controls(struct gspca_dev *gspca_dev) + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) { - 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, 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; + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) { + konica_stream_off(gspca_dev); + reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG); + konica_stream_on(gspca_dev); } return 0; } +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + + 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) @@ -480,7 +628,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/m5602/m5602_core.c b/trunk/drivers/media/video/gspca/m5602/m5602_core.c index ed22638978ce..0c4493675438 100644 --- a/trunk/drivers/media/video/gspca/m5602/m5602_core.c +++ b/trunk/drivers/media/video/gspca/m5602/m5602_core.c @@ -400,7 +400,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif .disconnect = m5602_disconnect }; diff --git a/trunk/drivers/media/video/gspca/mars.c b/trunk/drivers/media/video/gspca/mars.c index ff2c5abf115b..ec7b21ee79fb 100644 --- a/trunk/drivers/media/video/gspca/mars.c +++ b/trunk/drivers/media/video/gspca/mars.c @@ -30,8 +30,6 @@ MODULE_AUTHOR("Michel Xhaard "); 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 */ @@ -44,6 +42,13 @@ 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]; }; @@ -189,6 +194,9 @@ 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; } @@ -206,7 +214,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, 6); + v4l2_ctrl_handler_init(hdl, 7); 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, @@ -221,6 +229,9 @@ 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; @@ -233,11 +244,13 @@ 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; } @@ -256,7 +269,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, QUALITY); + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); data = gspca_dev->usb_buf; @@ -398,6 +411,31 @@ 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, @@ -407,6 +445,8 @@ 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 -- */ diff --git a/trunk/drivers/media/video/gspca/mr97310a.c b/trunk/drivers/media/video/gspca/mr97310a.c index 8f4714df5990..d73e5bd3dbf7 100644 --- a/trunk/drivers/media/video/gspca/mr97310a.c +++ b/trunk/drivers/media/video/gspca/mr97310a.c @@ -67,7 +67,6 @@ #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 @@ -85,15 +84,17 @@ 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 { @@ -104,6 +105,132 @@ 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, @@ -354,6 +481,7 @@ 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; @@ -487,6 +615,52 @@ 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; } @@ -778,6 +952,11 @@ 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); } @@ -792,25 +971,37 @@ static void sd_stopN(struct gspca_dev *gspca_dev) lcd_stop(gspca_dev); } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { 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 (val > 0) { + if (sd->brightness > 0) { sensor_write1(gspca_dev, sign_reg, 0x00); + val = sd->brightness; } else { sensor_write1(gspca_dev, sign_reg, 0x01); - val = 257 - val; + val = (257 - sd->brightness); } /* Use lookup table for funky Argus QuickClix brightness */ if (sd->do_lcd_stop) @@ -819,20 +1010,23 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 val) sensor_write1(gspca_dev, value_reg, val); } -static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv) +static void setexposure(struct gspca_dev *gspca_dev) { 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 = (expo * 9267) / 10000 + 300; + exposure = (sd->exposure * 9267) / 10000 + 300; sensor_write1(gspca_dev, 3, exposure >> 4); sensor_write1(gspca_dev, 4, exposure & 0x0f); } else if (sd->sensor_type == 2) { - exposure = expo; + exposure = sd->exposure; exposure >>= 3; sensor_write1(gspca_dev, 3, exposure >> 8); sensor_write1(gspca_dev, 4, exposure & 0xff); @@ -844,11 +1038,11 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv) Note our 0 - 4095 exposure is mapped to 0 - 511 milliseconds exposure time */ - u8 clockdiv = (60 * expo + 7999) / 8000; + u8 clockdiv = (60 * sd->exposure + 7999) / 8000; /* Limit framerate to not exceed usb bandwidth */ - if (clockdiv < min_clockdiv && gspca_dev->width >= 320) - clockdiv = min_clockdiv; + if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320) + clockdiv = sd->min_clockdiv; else if (clockdiv < 2) clockdiv = 2; @@ -857,7 +1051,7 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv) /* Frame exposure time in ms = 1000 * clockdiv / 60 -> exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ - exposure = (60 * 511 * expo) / (8000 * clockdiv); + exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv); if (exposure > 511) exposure = 511; @@ -871,148 +1065,125 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv) } } -static void setgain(struct gspca_dev *gspca_dev, s32 val) +static void setgain(struct gspca_dev *gspca_dev) { 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, val); + sensor_write1(gspca_dev, 0x0e, sd->gain); else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2) for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) { - sensor_write1(gspca_dev, gainreg, val >> 8); - sensor_write1(gspca_dev, gainreg + 1, val & 0xff); + sensor_write1(gspca_dev, gainreg, sd->gain >> 8); + sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff); } else - sensor_write1(gspca_dev, 0x10, val); + sensor_write1(gspca_dev, 0x10, sd->gain); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { - sensor_write1(gspca_dev, 0x1c, val); + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX)) + return; + + sensor_write1(gspca_dev, 0x1c, sd->contrast); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; + struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_err = 0; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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; + *val = sd->brightness; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +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_init_controls(struct gspca_dev *gspca_dev) +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) { - 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; + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 4); +static int sd_setgain(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->gain = val; + if (gspca_dev->streaming) + setgain(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); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - if (has_exposure && has_clockdiv) - v4l2_ctrl_cluster(2, &sd->exposure); +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; return 0; } @@ -1050,9 +1221,10 @@ 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, @@ -1084,7 +1256,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/nw80x.c b/trunk/drivers/media/video/gspca/nw80x.c index 44c9964b1b3e..42e021931e60 100644 --- a/trunk/drivers/media/video/gspca/nw80x.c +++ b/trunk/drivers/media/video/gspca/nw80x.c @@ -32,10 +32,22 @@ 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 @@ -1655,13 +1667,17 @@ static int swap_bits(int v) return r; } -static void setgain(struct gspca_dev *gspca_dev, u8 val) +static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 v[2]; + u8 val, 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: @@ -1674,11 +1690,13 @@ static void setgain(struct gspca_dev *gspca_dev, u8 val) } } -static void setexposure(struct gspca_dev *gspca_dev, s32 val) +static void setexposure(struct gspca_dev *gspca_dev) { 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; @@ -1695,12 +1713,14 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 val) } } -static void setautogain(struct gspca_dev *gspca_dev, s32 val) +static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int w, h; - if (!val) { + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN)) + return; + if (!sd->ctrls[AUTOGAIN].val) { sd->ag_cnt = -1; return; } @@ -1743,6 +1763,7 @@ 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; @@ -1813,7 +1834,33 @@ 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; } @@ -1878,6 +1925,9 @@ 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; @@ -1937,6 +1987,24 @@ 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; @@ -1956,100 +2024,62 @@ static void do_autogain(struct gspca_dev *gspca_dev) switch (sd->webcam) { case P35u: - gspca_coarse_grained_expo_autogain(gspca_dev, luma, 100, 5); + coarse_grained_expo_autogain(gspca_dev, luma, 100, 5); break; default: - gspca_expo_autogain(gspca_dev, luma, 100, 5, 230, 0); + auto_gain_n_exposure(gspca_dev, luma, 100, 5, 230, 0); break; } } - -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, +/* 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_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, @@ -2087,7 +2117,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/ov519.c b/trunk/drivers/media/video/gspca/ov519.c index bfc7cefa59f8..183457c5cfdb 100644 --- a/trunk/drivers/media/video/gspca/ov519.c +++ b/trunk/drivers/media/video/gspca/ov519.c @@ -60,20 +60,25 @@ 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 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; - }; + struct gspca_ctrl ctrls[NCTRL]; u8 packet_nr; @@ -96,6 +101,7 @@ struct sd { /* Determined by sensor type */ u8 sif; + u8 quality; #define QUALITY_MIN 50 #define QUALITY_MAX 70 #define QUALITY_DEF 50 @@ -139,114 +145,211 @@ enum sensors { really should move the sensor drivers to v4l2 sub drivers. */ #include "w996Xcf.c" -/* 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; -}; - -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, +/* 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, }, - [SEN_OV66308AF] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, +[CONTRAST] = { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set_control = setcontrast, }, - [SEN_OV7610] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set_control = setexposure, }, - [SEN_OV7620] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, +[COLORS] = { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set_control = setcolors, }, - [SEN_OV7620AE] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_autobright = 1, - .has_freq = 1, +/* 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, }, - [SEN_OV7640] = { - .has_brightness = 1, - .has_sat = 1, - .has_freq = 1, +[VFLIP] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set_control = sethvflip, }, - [SEN_OV7648] = { - .has_brightness = 1, - .has_sat = 1, - .has_freq = 1, +[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, }, - [SEN_OV7660] = { - .has_brightness = 1, - .has_contrast = 1, - .has_sat = 1, - .has_hvflip = 1, - .has_freq = 1, +[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, }, - [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, +[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 */ +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 v4l2_pix_format ov519_vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, @@ -3203,11 +3306,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, s32 val) +static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05); + i2c_w_mask(sd, 0x13, sd->ctrls[AUTOGAIN].val ? 0x05 : 0x00, 0x05); } /* this function is called at probe time */ @@ -3248,6 +3351,8 @@ 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; @@ -3362,6 +3467,8 @@ 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: @@ -3387,6 +3494,8 @@ 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: @@ -3413,12 +3522,26 @@ 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: @@ -4054,14 +4177,15 @@ 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, s32 hflip, s32 vflip) +static void sethvflip(struct gspca_dev *gspca_dev) { 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 * hflip | OV7670_MVFP_VFLIP * vflip, + OV7670_MVFP_MIRROR * sd->ctrls[HFLIP].val + | OV7670_MVFP_VFLIP * sd->ctrls[VFLIP].val, OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP); if (sd->gspca_dev.streaming) reg_w(sd, OV519_R51_RESET1, 0x00); /* restart stream */ @@ -4209,6 +4333,23 @@ 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; @@ -4464,9 +4605,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* -- management routines -- */ -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { 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}}, @@ -4484,6 +4626,7 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 val) {0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}} }; + val = sd->ctrls[BRIGHTNESS].val; switch (sd->sensor) { case SEN_OV8610: case SEN_OV7610: @@ -4497,7 +4640,9 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 val) break; case SEN_OV7620: case SEN_OV7620AE: - i2c_w(sd, OV7610_REG_BRT, val); + /* 7620 doesn't like manual changes when in auto mode */ + if (!sd->ctrls[AUTOBRIGHT].val) + i2c_w(sd, OV7610_REG_BRT, val); break; case SEN_OV7660: write_i2c_regvals(sd, brit_7660[val], @@ -4511,9 +4656,10 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 val) } } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { 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}, @@ -4573,6 +4719,7 @@ static void setcontrast(struct gspca_dev *gspca_dev, s32 val) {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}}, }; + val = sd->ctrls[CONTRAST].val; switch (sd->sensor) { case SEN_OV7610: case SEN_OV6620: @@ -4613,16 +4760,18 @@ static void setcontrast(struct gspca_dev *gspca_dev, s32 val) } } -static void setexposure(struct gspca_dev *gspca_dev, s32 val) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - i2c_w(sd, 0x10, val); + if (!sd->ctrls[AUTOGAIN].val) + i2c_w(sd, 0x10, sd->ctrls[EXPOSURE].val); } -static void setcolors(struct gspca_dev *gspca_dev, s32 val) +static void setcolors(struct gspca_dev *gspca_dev) { 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}}, @@ -4636,6 +4785,7 @@ static void setcolors(struct gspca_dev *gspca_dev, s32 val) {0x53, 0x66}, {0x54, 0x8e}}, }; + val = sd->ctrls[COLORS].val; switch (sd->sensor) { case SEN_OV8610: case SEN_OV7610: @@ -4669,18 +4819,34 @@ static void setcolors(struct gspca_dev *gspca_dev, s32 val) } } -static void setautobright(struct gspca_dev *gspca_dev, s32 val) +static void setautobright(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10); + i2c_w_mask(sd, 0x2d, sd->ctrls[AUTOBRIGHT].val ? 0x10 : 0x00, 0x10); } -static void setfreq_i(struct sd *sd, s32 val) +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) { if (sd->sensor == SEN_OV7660 || sd->sensor == SEN_OV7670) { - switch (val) { + switch (sd->ctrls[FREQ].val) { case 0: /* Banding filter disabled */ i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT); break; @@ -4702,7 +4868,7 @@ static void setfreq_i(struct sd *sd, s32 val) break; } } else { - switch (val) { + switch (sd->ctrls[FREQ].val) { case 0: /* Banding filter disabled */ i2c_w_mask(sd, 0x2d, 0x00, 0x04); i2c_w_mask(sd, 0x2a, 0x00, 0x80); @@ -4734,28 +4900,56 @@ static void setfreq_i(struct sd *sd, s32 val) } } } - -static void setfreq(struct gspca_dev *gspca_dev, s32 val) +static void setfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - setfreq_i(sd, val); + setfreq_i(sd); /* 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 -ENOTTY; + return -EINVAL; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + jcomp->quality = sd->quality; jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; return 0; @@ -4767,161 +4961,38 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; if (sd->bridge != BRIDGE_W9968CF) - return -ENOTTY; - - v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); - return 0; -} - -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; - - gspca_dev->usb_err = 0; + return -EINVAL; - 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; -} + if (gspca_dev->streaming) + return -EBUSY; -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .g_volatile_ctrl = sd_g_volatile_ctrl, - .s_ctrl = sd_s_ctrl, -}; + 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_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); + /* Return resulting jcomp params to app */ + sd_get_jcomp(gspca_dev, jcomp); - 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) @@ -4981,7 +5052,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/ov534.c b/trunk/drivers/media/video/gspca/ov534.c index bb09d7884b89..80c81dd6d68b 100644 --- a/trunk/drivers/media/video/gspca/ov534.c +++ b/trunk/drivers/media/video/gspca/ov534.c @@ -35,7 +35,6 @@ #include "gspca.h" #include -#include #define OV534_REG_ADDRESS 0xf1 /* sensor address */ #define OV534_REG_SUBADDR 0xf2 @@ -54,28 +53,29 @@ MODULE_AUTHOR("Antonio Ospite "); 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 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; + struct gspca_ctrl ctrls[NCTRLS]; __u32 last_pts; u16 last_fid; @@ -89,9 +89,181 @@ 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, @@ -800,10 +972,12 @@ 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, s32 val) +static void sethue(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int val; + val = sd->ctrls[HUE].val; if (sd->sensor == SENSOR_OV767x) { /* TBD */ } else { @@ -840,10 +1014,12 @@ static void sethue(struct gspca_dev *gspca_dev, s32 val) } } -static void setsaturation(struct gspca_dev *gspca_dev, s32 val) +static void setsaturation(struct gspca_dev *gspca_dev) { 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] = { @@ -864,10 +1040,12 @@ static void setsaturation(struct gspca_dev *gspca_dev, s32 val) } } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { 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; @@ -877,18 +1055,27 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 val) } } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { 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, s32 val) +static void setgain(struct gspca_dev *gspca_dev) { + 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; @@ -910,15 +1097,15 @@ static void setgain(struct gspca_dev *gspca_dev, s32 val) sccb_reg_write(gspca_dev, 0x00, val); } -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) +static void setexposure(struct gspca_dev *gspca_dev) { 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] */ @@ -936,23 +1123,11 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 val) } } -static s32 getexposure(struct gspca_dev *gspca_dev) +static void setagc(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - 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) { + if (sd->ctrls[AGC].val) { sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) | 0x04); sccb_reg_write(gspca_dev, 0x64, @@ -962,14 +1137,16 @@ static void setagc(struct gspca_dev *gspca_dev, s32 val) 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, s32 val) +static void setawb(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (val) { + if (sd->ctrls[AWB].val) { sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) | 0x02); if (sd->sensor == SENSOR_OV772x) @@ -984,7 +1161,7 @@ static void setawb(struct gspca_dev *gspca_dev, s32 val) } } -static void setaec(struct gspca_dev *gspca_dev, s32 val) +static void setaec(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 data; @@ -992,25 +1169,31 @@ static void setaec(struct gspca_dev *gspca_dev, s32 val) data = sd->sensor == SENSOR_OV767x ? 0x05 : /* agc + aec */ 0x01; /* agc */ - switch (val) { - case V4L2_EXPOSURE_AUTO: + if (sd->ctrls[AEC].val) sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) | data); - break; - case V4L2_EXPOSURE_MANUAL: + else { sccb_reg_write(gspca_dev, 0x13, sccb_reg_read(gspca_dev, 0x13) & ~data); - break; + if (sd->sensor == SENSOR_OV767x) + sd->ctrls[EXPOSURE].val = + sccb_reg_read(gspca_dev, 10); /* aech */ + else + setexposure(gspca_dev); } } -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +static void setsharpness(struct gspca_dev *gspca_dev) { + 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, s32 hflip, s32 vflip) +static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; @@ -1018,27 +1201,28 @@ static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) if (sd->sensor == SENSOR_OV767x) { val = sccb_reg_read(gspca_dev, 0x1e); /* mvfp */ val &= ~0x30; - if (hflip) + if (sd->ctrls[HFLIP].val) val |= 0x20; - if (vflip) + if (sd->ctrls[VFLIP].val) val |= 0x10; sccb_reg_write(gspca_dev, 0x1e, val); } else { val = sccb_reg_read(gspca_dev, 0x0c); val &= ~0xc0; - if (hflip == 0) + if (sd->ctrls[HFLIP].val == 0) val |= 0x40; - if (vflip == 0) + if (sd->ctrls[VFLIP].val == 0) val |= 0x80; sccb_reg_write(gspca_dev, 0x0c, val); } } -static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) +static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 val; - val = val ? 0x9e : 0x00; + val = sd->ctrls[LIGHTFREQ].val ? 0x9e : 0x00; if (sd->sensor == SENSOR_OV767x) { sccb_reg_write(gspca_dev, 0x2a, 0x00); if (val) @@ -1057,6 +1241,8 @@ 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); @@ -1065,195 +1251,6 @@ 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) { @@ -1289,6 +1286,24 @@ 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 { @@ -1351,23 +1366,22 @@ static int sd_start(struct gspca_dev *gspca_dev) set_frame_rate(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)); + 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); ov534_set_led(gspca_dev, 1); ov534_reg_write(gspca_dev, 0xe0, 0x00); @@ -1469,6 +1483,25 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } 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) @@ -1503,12 +1536,14 @@ 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, }; @@ -1537,7 +1572,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/ov534_9.c b/trunk/drivers/media/video/gspca/ov534_9.c index c4cd028fe0b4..1fd41f0d2e95 100644 --- a/trunk/drivers/media/video/gspca/ov534_9.c +++ b/trunk/drivers/media/video/gspca/ov534_9.c @@ -47,9 +47,22 @@ MODULE_AUTHOR("Jean-Francois Moine "); 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; @@ -62,6 +75,103 @@ 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, @@ -994,14 +1104,16 @@ static void set_led(struct gspca_dev *gspca_dev, int status) } } -static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) +static void setbrightness(struct gspca_dev *gspca_dev) { 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 = brightness; + sval = sd->ctrls[BRIGHTNESS].val; val = 0x76; val += sval; sccb_write(gspca_dev, 0x24, val); @@ -1016,7 +1128,7 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) val = 0xe6; sccb_write(gspca_dev, 0x26, val); } else { - val = brightness; + val = sd->ctrls[BRIGHTNESS].val; if (val < 8) val = 15 - val; /* f .. 8 */ else @@ -1026,32 +1138,43 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) } } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { + 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 */ - val << 4); + sd->ctrls[CONTRAST].val << 4); } -static void setautogain(struct gspca_dev *gspca_dev, s32 autogain) +static void setautogain(struct gspca_dev *gspca_dev) { + 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 (autogain) + if (sd->ctrls[AUTOGAIN].val) val |= 0x05; /* agc & aec */ else val &= 0xfa; sccb_write(gspca_dev, 0x13, val); } -static void setexposure(struct gspca_dev *gspca_dev, s32 exposure) +static void setexposure(struct gspca_dev *gspca_dev) { - static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; + struct sd *sd = (struct sd *) gspca_dev; u8 val; + static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; - sccb_write(gspca_dev, 0x10, expo[exposure]); /* aec[9:2] */ + if (gspca_dev->ctrl_dis & (1 << EXPOSURE)) + return; + sccb_write(gspca_dev, 0x10, /* aec[9:2] */ + expo[sd->ctrls[EXPOSURE].val]); val = sccb_read(gspca_dev, 0x13); /* com8 */ sccb_write(gspca_dev, 0xff, 0x00); @@ -1062,8 +1185,14 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 exposure) sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */ } -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +static void setsharpness(struct gspca_dev *gspca_dev) { + 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); @@ -1080,8 +1209,9 @@ static void setsharpness(struct gspca_dev *gspca_dev, s32 val) sccb_write(gspca_dev, 0x42, val & 0xbf); } -static void setsatur(struct gspca_dev *gspca_dev, s32 val) +static void setsatur(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 val1, val2, val3; static const u8 matrix[5][2] = { {0x14, 0x38}, @@ -1091,8 +1221,10 @@ static void setsatur(struct gspca_dev *gspca_dev, s32 val) {0x48, 0x90} }; - val1 = matrix[val][0]; - val2 = matrix[val][1]; + if (gspca_dev->ctrl_dis & (1 << SATUR)) + return; + val1 = matrix[sd->ctrls[SATUR].val][0]; + val2 = matrix[sd->ctrls[SATUR].val][1]; val3 = val1 + val2; sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */ sccb_write(gspca_dev, 0x50, val3); @@ -1107,13 +1239,16 @@ static void setsatur(struct gspca_dev *gspca_dev, s32 val) sccb_write(gspca_dev, 0x41, val1); } -static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq) +static void setlightfreq(struct gspca_dev *gspca_dev) { + 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 (freq == 0) { + if (sd->ctrls[LIGHTFREQ].val == 0) { sccb_write(gspca_dev, 0x13, val & 0xdf); return; } @@ -1121,7 +1256,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq) val = sccb_read(gspca_dev, 0x42); /* com17 */ sccb_write(gspca_dev, 0xff, 0x00); - if (freq == 1) + if (sd->ctrls[LIGHTFREQ].val == 1) val |= 0x01; else val &= 0xfe; @@ -1132,6 +1267,13 @@ static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq) 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; } @@ -1188,6 +1330,9 @@ 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; @@ -1213,6 +1358,16 @@ 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); @@ -1235,9 +1390,10 @@ static int sd_start(struct gspca_dev *gspca_dev) if (sd->sensor == SENSOR_OV971x) return gspca_dev->usb_err; - if (sd->sensor == SENSOR_OV562x) + else if (sd->sensor == SENSOR_OV562x) { + setbrightness(gspca_dev); return gspca_dev->usb_err; - + } switch (gspca_dev->curr_mode) { case QVGA_MODE: /* 320x240 */ sccb_w_array(gspca_dev, ov965x_start_1_vga, @@ -1281,6 +1437,13 @@ 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); @@ -1378,94 +1541,38 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } while (remaining_len > 0); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) { - 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; + switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: - 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); + 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; + } 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; - - 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; + return -EINVAL; } /* 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 -- */ @@ -1493,7 +1600,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/pac207.c b/trunk/drivers/media/video/gspca/pac207.c index d236d1791f78..fa661c6d6d55 100644 --- a/trunk/drivers/media/video/gspca/pac207.c +++ b/trunk/drivers/media/video/gspca/pac207.c @@ -462,7 +462,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/pac7302.c b/trunk/drivers/media/video/gspca/pac7302.c index 4877f7ab3d59..a0369a58c4bb 100644 --- a/trunk/drivers/media/video/gspca/pac7302.c +++ b/trunk/drivers/media/video/gspca/pac7302.c @@ -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 , " "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 { /* 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; - }; + struct gspca_ctrl ctrls[NCTRLS]; + u8 flags; #define FL_HFLIP 0x01 /* mirrored by default */ #define FL_VFLIP 0x02 /* vertical flipped by default */ @@ -119,6 +119,160 @@ 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, @@ -362,6 +516,8 @@ 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; } @@ -380,9 +536,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->brightness->val - sd->brightness->maximum) - * 150 / sd->brightness->maximum; /* 200 ? */ - v -= delta[i] * sd->contrast->val / sd->contrast->maximum; + v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX) + * 150 / BRIGHTNESS_MAX; /* 200 ? */ + v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX; if (v < 0) v = 0; else if (v > 0xff) @@ -405,8 +561,7 @@ 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->saturation->val / sd->saturation->maximum; - v += b[i]; + v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i]; reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); reg_w(gspca_dev, 0x0f + 2 * i + 1, v); } @@ -418,7 +573,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->white_balance->val); + reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val); reg_w(gspca_dev, 0xdc, 0x01); } @@ -428,7 +583,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->red_balance->val); + reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val); reg_w(gspca_dev, 0xdc, 0x01); } @@ -438,21 +593,22 @@ 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->blue_balance->val); + reg_w(gspca_dev, 0xc7, sd->ctrls[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 (gspca_dev->gain->val < 32) { - reg10 = gspca_dev->gain->val; + if (sd->ctrls[GAIN].val < 32) { + reg10 = sd->ctrls[GAIN].val; reg12 = 0; } else { reg10 = 31; - reg12 = gspca_dev->gain->val - 31; + reg12 = sd->ctrls[GAIN].val - 31; } reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ @@ -465,6 +621,7 @@ 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; @@ -473,7 +630,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 * gspca_dev->exposure->val + 1999) / 2000; + clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000; /* * Note clockdiv = 3 also works, but when running at 30 fps, depending @@ -498,7 +655,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 = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv); + exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv); /* 0 = use full frametime, 448 = no exposure, reverse it */ exposure = 448 - exposure; @@ -511,15 +668,37 @@ 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->hflip->val; + hflip = sd->ctrls[HFLIP].val; if (sd->flags & FL_HFLIP) hflip = !hflip; - vflip = sd->vflip->val; + vflip = sd->ctrls[VFLIP].val; if (sd->flags & FL_VFLIP) vflip = !vflip; @@ -538,112 +717,6 @@ 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; @@ -655,13 +728,11 @@ static int sd_start(struct gspca_dev *gspca_dev) setwhitebalance(gspca_dev); setredbalance(gspca_dev); setbluebalance(gspca_dev); - setexposure(gspca_dev); - setgain(gspca_dev); + setautogain(gspca_dev); sethvflip(gspca_dev); sd->sof_read = 0; - sd->autogain_ignore_frames = 0; - atomic_set(&sd->avg_lum, 270 + sd->brightness->val); + atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val); /* start stream */ reg_w(gspca_dev, 0xff, 0x01); @@ -687,6 +758,9 @@ 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; @@ -700,13 +774,11 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) { sd->autogain_ignore_frames--; } else { - desired_lum = 270 + sd->brightness->val; + desired_lum = 270 + sd->ctrls[BRIGHTNESS].val; - 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; + auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + deadzone, GAIN_KNEE, EXPOSURE_KNEE); + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } } @@ -872,9 +944,10 @@ 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, @@ -925,7 +998,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/pac7311.c b/trunk/drivers/media/video/gspca/pac7311.c index ba3558d3f017..115da169f32a 100644 --- a/trunk/drivers/media/video/gspca/pac7311.c +++ b/trunk/drivers/media/video/gspca/pac7311.c @@ -694,7 +694,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/se401.c b/trunk/drivers/media/video/gspca/se401.c index a33cb78a839c..bb70092c2229 100644 --- a/trunk/drivers/media/video/gspca/se401.c +++ b/trunk/drivers/media/video/gspca/se401.c @@ -45,6 +45,15 @@ MODULE_AUTHOR("Hans de Goede "); 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, @@ -55,11 +64,7 @@ enum { /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct { /* exposure/freq control cluster */ - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *freq; - }; - bool has_brightness; + struct gspca_ctrl ctrls[NCTRL]; struct v4l2_pix_format fmts[MAX_MODES]; int pixels_read; int packet_read; @@ -72,6 +77,60 @@ 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) @@ -165,15 +224,22 @@ 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, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { + 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, val, 0); + se401_write_req(gspca_dev, SE401_REQ_SET_BRT, + sd->ctrls[BRIGHTNESS].val, 0); } -static void setgain(struct gspca_dev *gspca_dev, s32 val) +static void setgain(struct gspca_dev *gspca_dev) { - u16 gain = 63 - val; + struct sd *sd = (struct sd *) gspca_dev; + u16 gain = 63 - sd->ctrls[GAIN].val; /* red color gain */ se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain); @@ -183,10 +249,10 @@ static void setgain(struct gspca_dev *gspca_dev, s32 val) se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain); } -static void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int integration = val << 6; + int integration = sd->ctrls[EXPOSURE].val << 6; u8 expose_h, expose_m, expose_l; /* Do this before the set_feature calls, for proper timing wrt @@ -196,9 +262,9 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq) through so be it */ sd->expo_change_state = EXPO_CHANGED; - if (freq == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) + if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) integration = integration - integration % 106667; - if (freq == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) + if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) integration = integration - integration % 88889; expose_h = (integration >> 16); @@ -309,12 +375,15 @@ 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); - sd->has_brightness = !!gspca_dev->usb_err; - gspca_dev->usb_err = 0; + if (gspca_dev->usb_err) { + gspca_dev->ctrl_dis = (1 << BRIGHTNESS); + gspca_dev->usb_err = 0; + } return 0; } @@ -373,6 +442,9 @@ 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; @@ -594,6 +666,27 @@ 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) { @@ -621,73 +714,19 @@ 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 @@ -730,7 +769,6 @@ 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, diff --git a/trunk/drivers/media/video/gspca/sn9c2028.c b/trunk/drivers/media/video/gspca/sn9c2028.c index 03fa3fd940b4..478533cb1152 100644 --- a/trunk/drivers/media/video/gspca/sn9c2028.c +++ b/trunk/drivers/media/video/gspca/sn9c2028.c @@ -40,6 +40,10 @@ 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, @@ -691,6 +695,8 @@ 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, @@ -728,7 +734,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/sonixb.c b/trunk/drivers/media/video/gspca/sonixb.c index fd1f8d2d3b0b..e2bdf8f632f4 100644 --- a/trunk/drivers/media/video/gspca/sonixb.c +++ b/trunk/drivers/media/video/gspca/sonixb.c @@ -56,16 +56,26 @@ MODULE_AUTHOR("Jean-François Moine "); 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 v4l2_ctrl *brightness; - struct v4l2_ctrl *plfreq; + struct gspca_ctrl ctrls[NCTRLS]; atomic_t avg_lum; int prev_avg_lum; - int exposure_knee; + int exp_too_low_cnt; + int exp_too_high_cnt; int header_read; u8 header[12]; /* Header without sof marker */ @@ -97,16 +107,24 @@ 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_SIF 0x01 /* sif or vga */ +#define F_GAIN 0x01 /* has gain */ +#define F_SIF 0x02 /* sif or vga */ +#define F_COARSE_EXPO 0x04 /* exposure control is coarse */ /* 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 */ @@ -115,12 +133,12 @@ struct sensor_data { #define SYS_CLK 0x04 -#define SENS(bridge, sensor, _flags, _sensor_addr) \ +#define SENS(bridge, sensor, _flags, _ctrl_dis, _sensor_addr) \ { \ .bridge_init = bridge, \ .sensor_init = sensor, \ .sensor_init_size = sizeof(sensor), \ - .flags = _flags, .sensor_addr = _sensor_addr \ + .flags = _flags, .ctrl_dis = _ctrl_dis, .sensor_addr = _sensor_addr \ } /* We calculate the autogain at the end of the transfer of a frame, at this @@ -129,6 +147,87 @@ 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, @@ -433,27 +532,25 @@ static const __u8 tas5130_sensor_init[][8] = { }; static const struct sensor_data sensor_data[] = { - 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), +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), }; /* get one byte in gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, __u16 value) { - int res; - - if (gspca_dev->usb_err < 0) - return; - - res = usb_control_msg(gspca_dev->dev, + usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, @@ -461,12 +558,6 @@ 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, @@ -474,13 +565,14 @@ static void reg_w(struct gspca_dev *gspca_dev, const __u8 *buffer, int len) { - int res; - - if (gspca_dev->usb_err < 0) +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow"); return; - + } +#endif memcpy(gspca_dev->usb_buf, buffer, len); - res = usb_control_msg(gspca_dev->dev, + usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0x08, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, @@ -488,48 +580,30 @@ 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 void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) +static int 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) { - dev_err(gspca_dev->v4l2_dev.dev, - "i2c write error\n"); - gspca_dev->usb_err = -EIO; - } - return; + if (gspca_dev->usb_buf[0] & 0x08) + return -1; + return 0; } } - - dev_err(gspca_dev->v4l2_dev.dev, "i2c write timeout\n"); - gspca_dev->usb_err = -EIO; + return -1; } 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) @@ -550,10 +624,11 @@ static void setbrightness(struct gspca_dev *gspca_dev) /* change reg 0x06 */ i2cOV[1] = sensor_data[sd->sensor].sensor_addr; - i2cOV[3] = sd->brightness->val; - i2c_w(gspca_dev, i2cOV); + i2cOV[3] = sd->ctrls[BRIGHTNESS].val; + if (i2c_w(gspca_dev, i2cOV) < 0) + goto err; break; - } + } case SENSOR_PAS106: case SENSOR_PAS202: { __u8 i2cpbright[] = @@ -567,49 +642,54 @@ static void setbrightness(struct gspca_dev *gspca_dev) i2cpdoit[2] = 0x13; } - if (sd->brightness->val < 127) { + if (sd->ctrls[BRIGHTNESS].val < 127) { /* change reg 0x0b, signreg */ i2cpbright[3] = 0x01; /* set reg 0x0c, offset */ - i2cpbright[4] = 127 - sd->brightness->val; + i2cpbright[4] = 127 - sd->ctrls[BRIGHTNESS].val; } else - i2cpbright[4] = sd->brightness->val - 127; + i2cpbright[4] = sd->ctrls[BRIGHTNESS].val - 127; - i2c_w(gspca_dev, i2cpbright); - i2c_w(gspca_dev, i2cpdoit); - break; - } - default: + if (i2c_w(gspca_dev, i2cpbright) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; break; + } } + return; +err: + PDEBUG(D_ERR, "i2c error brightness"); } -static void setgain(struct gspca_dev *gspca_dev) +static void setsensorgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 gain = gspca_dev->gain->val; + u8 gain = sd->ctrls[GAIN].val; switch (sd->sensor) { case SENSOR_HV7131D: { __u8 i2c[] = {0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17}; - i2c[3] = 0x3f - gain; - i2c[4] = 0x3f - gain; - i2c[5] = 0x3f - gain; + i2c[3] = 0x3f - (gain / 4); + i2c[4] = 0x3f - (gain / 4); + i2c[5] = 0x3f - (gain / 4); - i2c_w(gspca_dev, i2c); + if (i2c_w(gspca_dev, i2c) < 0) + goto err; break; - } + } case SENSOR_TAS5110C: case SENSOR_TAS5130CXX: { __u8 i2c[] = {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; i2c[4] = 255 - gain; - i2c_w(gspca_dev, i2c); + if (i2c_w(gspca_dev, i2c) < 0) + goto err; break; - } + } case SENSOR_TAS5110D: { __u8 i2c[] = { 0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 }; @@ -623,25 +703,23 @@ static void setgain(struct gspca_dev *gspca_dev) i2c[3] |= (gain & 0x04) << 3; i2c[3] |= (gain & 0x02) << 5; i2c[3] |= (gain & 0x01) << 7; - i2c_w(gspca_dev, i2c); + if (i2c_w(gspca_dev, i2c) < 0) + goto err; 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; - i2c_w(gspca_dev, i2c); + i2c[3] = gain >> 2; + if (i2c_w(gspca_dev, i2c) < 0) + goto err; break; - } + } case SENSOR_PAS106: case SENSOR_PAS202: { __u8 i2cpgain[] = @@ -659,27 +737,49 @@ static void setgain(struct gspca_dev *gspca_dev) i2cpdoit[2] = 0x13; } - i2cpgain[3] = gain; - i2cpcolorgain[3] = gain >> 1; - i2cpcolorgain[4] = gain >> 1; - i2cpcolorgain[5] = gain >> 1; - i2cpcolorgain[6] = gain >> 1; - - i2c_w(gspca_dev, i2cpgain); - i2c_w(gspca_dev, i2cpcolorgain); - i2c_w(gspca_dev, i2cpdoit); + i2cpgain[3] = gain >> 3; + i2cpcolorgain[3] = gain >> 4; + i2cpcolorgain[4] = gain >> 4; + i2cpcolorgain[5] = gain >> 4; + i2cpcolorgain[6] = gain >> 4; + + 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; break; + } } - 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); - } + 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); } } @@ -692,24 +792,31 @@ 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}; - u16 reg = gspca_dev->exposure->val; + /* 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; i2c[3] = reg >> 8; i2c[4] = reg & 0xff; - i2c_w(gspca_dev, i2c); + if (i2c_w(gspca_dev, i2c) != 0) + goto err; 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 = gspca_dev->exposure->val; + u8 reg = sd->ctrls[EXPOSURE].val; reg = (reg << 4) | 0x0b; reg_w(gspca_dev, 0x19, ®, 1); break; - } + } case SENSOR_OV6650: case SENSOR_OV7630: { /* The ov6650 / ov7630 have 2 registers which both influence @@ -741,7 +848,7 @@ static void setexposure(struct gspca_dev *gspca_dev) } else reg10_max = 0x41; - reg11 = (15 * gspca_dev->exposure->val + 999) / 1000; + reg11 = (15 * sd->ctrls[EXPOSURE].val + 999) / 1000; if (reg11 < 1) reg11 = 1; else if (reg11 > 16) @@ -754,16 +861,16 @@ static void setexposure(struct gspca_dev *gspca_dev) reg11 = 4; /* frame exposure time in ms = 1000 * reg11 / 30 -> - reg10 = (gspca_dev->exposure->val / 2) * reg10_max + reg10 = (sd->ctrls[EXPOSURE].val / 2) * reg10_max / (1000 * reg11 / 30) */ - reg10 = (gspca_dev->exposure->val * 15 * reg10_max) + reg10 = (sd->ctrls[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 (gspca_dev->autogain->val && reg10 < 10) + if (sd->ctrls[AUTOGAIN].val && reg10 < 10) reg10 = 10; else if (reg10 > reg10_max) reg10 = reg10_max; @@ -777,11 +884,12 @@ static void setexposure(struct gspca_dev *gspca_dev) if (sd->reg11 == reg11) i2c[0] = 0xa0; - i2c_w(gspca_dev, i2c); - if (gspca_dev->usb_err == 0) + if (i2c_w(gspca_dev, i2c) == 0) sd->reg11 = reg11; + else + goto err; break; - } + } case SENSOR_PAS202: { __u8 i2cpframerate[] = {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16}; @@ -801,25 +909,28 @@ 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 (gspca_dev->exposure->val < 200) { - i2cpexpo[3] = 255 - (gspca_dev->exposure->val * 255) + if (sd->ctrls[EXPOSURE].val < 200) { + i2cpexpo[3] = 255 - (sd->ctrls[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 = (gspca_dev->exposure->val - 200) + framerate_ctrl = (sd->ctrls[EXPOSURE].val - 200) * 1000 / 229 + 500; } i2cpframerate[3] = framerate_ctrl >> 6; i2cpframerate[4] = framerate_ctrl & 0x3f; - i2c_w(gspca_dev, i2cpframerate); - i2c_w(gspca_dev, i2cpexpo); - i2c_w(gspca_dev, i2cpdoit); + 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; break; - } + } case SENSOR_PAS106: { __u8 i2cpframerate[] = {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14}; @@ -831,40 +942,46 @@ static void setexposure(struct gspca_dev *gspca_dev) /* For values below 150 use partial frame exposure, above that use framerate ctrl */ - if (gspca_dev->exposure->val < 150) { - i2cpexpo[3] = 150 - gspca_dev->exposure->val; + if (sd->ctrls[EXPOSURE].val < 150) { + i2cpexpo[3] = 150 - sd->ctrls[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 = (gspca_dev->exposure->val - 150) + framerate_ctrl = (sd->ctrls[EXPOSURE].val - 150) * 1000 / 230 + 300; } i2cpframerate[3] = framerate_ctrl >> 4; i2cpframerate[4] = framerate_ctrl & 0x0f; - i2c_w(gspca_dev, i2cpframerate); - i2c_w(gspca_dev, i2cpexpo); - i2c_w(gspca_dev, i2cpdoit); - break; - } - default: + 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; break; + } } + return; +err: + PDEBUG(D_ERR, "i2c error exposure"); } static void setfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) { + switch (sd->sensor) { + case SENSOR_OV6650: + case 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->plfreq->val) { + switch (sd->ctrls[FREQ].val) { default: /* case 0: * no filter*/ /* case 2: * 60 hz */ @@ -876,17 +993,25 @@ static void setfreq(struct gspca_dev *gspca_dev) break; } i2c[1] = sensor_data[sd->sensor].sensor_addr; - i2c_w(gspca_dev, i2c); + if (i2c_w(gspca_dev, i2c) < 0) + PDEBUG(D_ERR, "i2c error setfreq"); + break; + } } } +#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 deadzone, desired_avg_lum, avg_lum; + int avg_lum = atomic_read(&sd->avg_lum); - avg_lum = atomic_read(&sd->avg_lum); - if (avg_lum == -1) + if ((gspca_dev->ctrl_dis & (1 << AUTOGAIN)) || + avg_lum == -1 || !sd->ctrls[AUTOGAIN].val) return; if (sd->autogain_ignore_frames > 0) { @@ -905,18 +1030,22 @@ static void do_autogain(struct gspca_dev *gspca_dev) desired_avg_lum = 13000; } - if (sd->brightness) - desired_avg_lum = sd->brightness->val * desired_avg_lum / 127; - - 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; + 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 (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; } } @@ -935,7 +1064,14 @@ 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); @@ -950,144 +1086,19 @@ 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) -{ - const __u8 stop = 0x09; /* Disable stream turn of LED */ - - 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; - } - - 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); + const __u8 stop = 0x09; /* Disable stream turn of LED */ - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; + 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; } - if (gspca_dev->autogain) - v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + reg_w(gspca_dev, 0x01, &stop, 1); return 0; } @@ -1231,10 +1242,10 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->frames_to_drop = 0; sd->autogain_ignore_frames = 0; - gspca_dev->exp_too_high_cnt = 0; - gspca_dev->exp_too_low_cnt = 0; + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; atomic_set(&sd->avg_lum, -1); - return gspca_dev->usb_err; + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -1376,6 +1387,37 @@ 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) { @@ -1419,9 +1461,10 @@ 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, @@ -1486,7 +1529,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/sonixj.c b/trunk/drivers/media/video/gspca/sonixj.c index 150b2df40f7f..f38faa9b37c3 100644 --- a/trunk/drivers/media/video/gspca/sonixj.c +++ b/trunk/drivers/media/video/gspca/sonixj.c @@ -3199,7 +3199,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/spca1528.c b/trunk/drivers/media/video/gspca/spca1528.c index 14d635277d71..070b9c33b517 100644 --- a/trunk/drivers/media/video/gspca/spca1528.c +++ b/trunk/drivers/media/video/gspca/spca1528.c @@ -33,11 +33,102 @@ 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, @@ -168,40 +259,58 @@ static void wait_status_1(struct gspca_dev *gspca_dev) gspca_dev->usb_err = -ETIME; } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { - reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, sd->brightness); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { - reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, sd->contrast); } -static void sethue(struct gspca_dev *gspca_dev, s32 val) +static void sethue(struct gspca_dev *gspca_dev) { - reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, sd->hue); } -static void setcolor(struct gspca_dev *gspca_dev, s32 val) +static void setcolor(struct gspca_dev *gspca_dev) { - reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, sd->color); } -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +static void setsharpness(struct gspca_dev *gspca_dev) { - reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, sd->sharpness); } /* 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; } @@ -261,6 +370,14 @@ 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); @@ -340,70 +457,103 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_dev->last_packet_type = DISCARD_PACKET; } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - 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_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; - } + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); return gspca_dev->usb_err; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; -static int sd_init_controls(struct gspca_dev *gspca_dev) + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) { - 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); - 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); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + 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; 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, @@ -437,7 +587,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/spca500.c b/trunk/drivers/media/video/gspca/spca500.c index 25cb68d0556d..103984708c77 100644 --- a/trunk/drivers/media/video/gspca/spca500.c +++ b/trunk/drivers/media/video/gspca/spca500.c @@ -30,12 +30,18 @@ MODULE_AUTHOR("Michel Xhaard "); 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 @@ -56,6 +62,59 @@ 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, @@ -582,6 +641,10 @@ 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; } @@ -610,7 +673,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, QUALITY); + jpeg_set_qual(sd->jpeg_hdr, sd->quality); if (sd->subtype == LogitechClickSmart310) { xmult = 0x16; @@ -871,79 +934,122 @@ 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, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + reg_w(gspca_dev, 0x00, 0x8167, - (__u8) (val - 128)); + (__u8) (sd->brightness - 128)); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { - reg_w(gspca_dev, 0x00, 0x8168, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0x00, 0x8168, sd->contrast); } -static void setcolors(struct gspca_dev *gspca_dev, s32 val) +static void setcolors(struct gspca_dev *gspca_dev) { - reg_w(gspca_dev, 0x00, 0x8169, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0x00, 0x8169, sd->colors); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - 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; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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; + *val = sd->brightness; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +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_init_controls(struct gspca_dev *gspca_dev) +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { - 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_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); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + 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; 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 -- */ @@ -983,7 +1089,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/spca501.c b/trunk/drivers/media/video/gspca/spca501.c index 3b7f777785b4..9c16821addd4 100644 --- a/trunk/drivers/media/video/gspca/spca501.c +++ b/trunk/drivers/media/video/gspca/spca501.c @@ -49,6 +49,91 @@ 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, @@ -1793,32 +1878,42 @@ static int write_vector(struct gspca_dev *gspca_dev, return 0; } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + reg_write(gspca_dev->dev, 0x00, 0x00, - (val >> 8) & 0xff); + (sd->contrast >> 8) & 0xff); reg_write(gspca_dev->dev, 0x00, 0x01, - val & 0xff); + sd->contrast & 0xff); } -static void setcolors(struct gspca_dev *gspca_dev, s32 val) +static void setcolors(struct gspca_dev *gspca_dev) { - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors); } -static void setblue_balance(struct gspca_dev *gspca_dev, s32 val) +static void setblue_balance(struct gspca_dev *gspca_dev) { - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance); } -static void setred_balance(struct gspca_dev *gspca_dev, s32 val) +static void setred_balance(struct gspca_dev *gspca_dev) { - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance); } /* this function is called at probe time */ @@ -1832,6 +1927,9 @@ 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; } @@ -1910,6 +2008,13 @@ 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; } @@ -1948,70 +2053,103 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - 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; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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; + *val = sd->brightness; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; -static int sd_init_controls(struct gspca_dev *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 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, 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); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + 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; 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, @@ -2047,7 +2185,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/spca505.c b/trunk/drivers/media/video/gspca/spca505.c index bc7d67c3cb04..1320f35e39f2 100644 --- a/trunk/drivers/media/video/gspca/spca505.c +++ b/trunk/drivers/media/video/gspca/spca505.c @@ -33,11 +33,34 @@ 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, @@ -610,6 +633,7 @@ 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; } @@ -627,8 +651,11 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) +static void setbrightness(struct gspca_dev *gspca_dev) { + 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); } @@ -679,9 +706,13 @@ 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]); - return reg_write(dev, SPCA50X_REG_USB, + ret = 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) @@ -725,49 +756,30 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - 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; + struct sd *sd = (struct sd *) gspca_dev; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { - 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); + struct sd *sd = (struct sd *) gspca_dev; - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + *val = sd->brightness; 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, @@ -800,7 +812,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/spca506.c b/trunk/drivers/media/video/gspca/spca506.c index 969bb5a4cd93..54eed87672d2 100644 --- a/trunk/drivers/media/video/gspca/spca506.c +++ b/trunk/drivers/media/video/gspca/spca506.c @@ -33,10 +33,83 @@ 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, @@ -208,11 +281,16 @@ 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; } @@ -486,93 +564,121 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, val, SAA7113_bright); + spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright); spca506_WriteI2c(gspca_dev, 0x01, 0x09); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, val, SAA7113_contrast); + spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast); spca506_WriteI2c(gspca_dev, 0x01, 0x09); } -static void setcolors(struct gspca_dev *gspca_dev, s32 val) +static void setcolors(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, val, SAA7113_saturation); + spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation); spca506_WriteI2c(gspca_dev, 0x01, 0x09); } -static void sethue(struct gspca_dev *gspca_dev, s32 val) +static void sethue(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + spca506_Initi2c(gspca_dev); - spca506_WriteI2c(gspca_dev, val, SAA7113_hue); + spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue); spca506_WriteI2c(gspca_dev, 0x01, 0x09); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - 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; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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; + *val = sd->brightness; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +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_init_controls(struct gspca_dev *gspca_dev) +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { - 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, 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); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + 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; 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, @@ -605,7 +711,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/spca508.c b/trunk/drivers/media/video/gspca/spca508.c index 1286b4170b88..df4e16996461 100644 --- a/trunk/drivers/media/video/gspca/spca508.c +++ b/trunk/drivers/media/video/gspca/spca508.c @@ -32,6 +32,8 @@ 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 @@ -41,6 +43,27 @@ 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, @@ -1388,6 +1411,7 @@ 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); @@ -1447,8 +1471,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } -static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) +static void setbrightness(struct gspca_dev *gspca_dev) { + 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); @@ -1456,50 +1483,31 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) reg_write(gspca_dev->dev, 0x8654, brightness); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - 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; + struct sd *sd = (struct sd *) gspca_dev; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - setbrightness(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; - -static int sd_init_controls(struct gspca_dev *gspca_dev) +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { - 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); + struct sd *sd = (struct sd *) gspca_dev; - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + *val = sd->brightness; 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, @@ -1533,7 +1541,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/spca561.c b/trunk/drivers/media/video/gspca/spca561.c index cfe71dd6747d..4a5f209ce719 100644 --- a/trunk/drivers/media/video/gspca/spca561.c +++ b/trunk/drivers/media/video/gspca/spca561.c @@ -31,17 +31,39 @@ MODULE_AUTHOR("Michel Xhaard "); 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 */ - struct { /* hue/contrast control cluster */ - struct v4l2_ctrl *contrast; - struct v4l2_ctrl *hue; - }; - struct v4l2_ctrl *autogain; + __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 #define EXPO12A_DEF 3 __u8 expo12a; /* expo/gain? for rev 12a */ @@ -439,6 +461,12 @@ 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; } @@ -463,49 +491,66 @@ static int sd_init_72a(struct gspca_dev *gspca_dev) return 0; } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +/* rev 72a only */ +static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - __u16 reg; + __u8 value; - if (sd->chip_revision == Rev012A) - reg = 0x8610; - else - reg = 0x8611; + value = sd->brightness; - 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 */ + /* 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 */ } -static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast) +static void setwhite(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; + __u16 white; __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 += contrast - 0x20; - blue += contrast - 0x20; - reg_w_val(dev, 0x8652, contrast + 0x20); /* Gr */ - reg_w_val(dev, 0x8654, contrast + 0x20); /* Gb */ + red += sd->contrast - 0x20; + blue += sd->contrast - 0x20; } - reg_w_val(dev, reg, red); - reg_w_val(dev, reg + 2, blue); + 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 */ } /* rev 12a only */ -static void setexposure(struct gspca_dev *gspca_dev, s32 val) +static void setexposure(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int i, expo = 0; /* Register 0x8309 controls exposure for the spca561, @@ -527,8 +572,8 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 val) int table[] = { 0, 450, 550, 625, EXPOSURE_MAX }; for (i = 0; i < ARRAY_SIZE(table) - 1; i++) { - if (val <= table[i + 1]) { - expo = val - table[i]; + if (sd->exposure <= table[i + 1]) { + expo = sd->exposure - table[i]; if (i) expo += 300; expo |= i << 11; @@ -542,27 +587,29 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 val) } /* rev 12a only */ -static void setgain(struct gspca_dev *gspca_dev, s32 val) +static void setgain(struct gspca_dev *gspca_dev) { + 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 (val < 64) - gspca_dev->usb_buf[0] = val; - else if (val < 128) - gspca_dev->usb_buf[0] = (val / 2) | 0x40; + 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; else - gspca_dev->usb_buf[0] = (val / 4) | 0xc0; + gspca_dev->usb_buf[0] = (sd->gain / 4) | 0xc0; gspca_dev->usb_buf[1] = 0; reg_w_buf(gspca_dev, 0x8335, 2); } -static void setautogain(struct gspca_dev *gspca_dev, s32 val) +static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (val) + if (sd->autogain) sd->ag_cnt = AG_CNT_START; else sd->ag_cnt = -1; @@ -597,6 +644,9 @@ 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); @@ -604,7 +654,6 @@ 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; @@ -634,10 +683,9 @@ 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); - setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue), - v4l2_ctrl_g_ctrl(sd->contrast)); + setcontrast(gspca_dev); /* setbrightness(gspca_dev); * fixme: bad values */ - setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); + setautogain(gspca_dev); reg_w_val(dev, 0x8112, 0x10 | 0x20); return 0; } @@ -771,96 +819,221 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +/* rev 72a only */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; + struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_err = 0; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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; + *val = sd->brightness; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +/* rev 72a only */ +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; -static int sd_init_controls_12a(struct gspca_dev *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 v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + struct sd *sd = (struct sd *) gspca_dev; - 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); + *val = sd->contrast; + return 0; +} - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } +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_init_controls_72a(struct gspca_dev *gspca_dev) +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) { - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + struct sd *sd = (struct sd *) gspca_dev; - 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); + *val = sd->autogain; + return 0; +} - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - v4l2_ctrl_cluster(2, &sd->contrast); +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 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, + }, +}; + /* sub-driver description */ static const struct sd_desc sd_desc_12a = { .name = MODULE_NAME, - .init_controls = sd_init_controls_12a, + .ctrls = sd_ctrls_12a, + .nctrls = ARRAY_SIZE(sd_ctrls_12a), .config = sd_config, .init = sd_init_12a, .start = sd_start_12a, @@ -872,7 +1045,8 @@ static const struct sd_desc sd_desc_12a = { }; static const struct sd_desc sd_desc_72a = { .name = MODULE_NAME, - .init_controls = sd_init_controls_72a, + .ctrls = sd_ctrls_72a, + .nctrls = ARRAY_SIZE(sd_ctrls_72a), .config = sd_config, .init = sd_init_72a, .start = sd_start_72a, @@ -929,7 +1103,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/sq905.c b/trunk/drivers/media/video/gspca/sq905.c index a8ac97931ad6..04f54654a026 100644 --- a/trunk/drivers/media/video/gspca/sq905.c +++ b/trunk/drivers/media/video/gspca/sq905.c @@ -433,7 +433,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/sq905c.c b/trunk/drivers/media/video/gspca/sq905c.c index 2c2f3d2f357f..f34ddb0570c8 100644 --- a/trunk/drivers/media/video/gspca/sq905c.c +++ b/trunk/drivers/media/video/gspca/sq905c.c @@ -340,7 +340,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/sq930x.c b/trunk/drivers/media/video/gspca/sq930x.c index 3e1e486af883..1a8ba9b3550a 100644 --- a/trunk/drivers/media/video/gspca/sq930x.c +++ b/trunk/drivers/media/video/gspca/sq930x.c @@ -36,10 +36,8 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct { /* exposure/gain control cluster */ - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; - }; + u16 expo; + u8 gain; u8 do_ctrl; u8 gpio[2]; @@ -57,6 +55,42 @@ 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, @@ -757,7 +791,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, s32 expo, s32 gain) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i, integclks, intstartclk, frameclks, min_frclk; @@ -765,7 +799,7 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain) u16 cmd; u8 buf[15]; - integclks = expo; + integclks = sd->expo; i = 0; cmd = SQ930_CTRL_SET_EXPOSURE; @@ -784,7 +818,7 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain) buf[i++] = intstartclk; buf[i++] = frameclks >> 8; buf[i++] = frameclks; - buf[i++] = gain; + buf[i++] = sd->gain; break; default: /* cmos */ /* case SENSOR_MI0360: */ @@ -800,7 +834,7 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain) buf[i++] = 0x35; /* reg = global gain */ buf[i++] = 0x00; /* val H */ buf[i++] = sensor->i2c_dum; - buf[i++] = 0x80 + gain / 2; /* val L */ + buf[i++] = 0x80 + sd->gain / 2; /* val L */ buf[i++] = 0x00; buf[i++] = 0x00; buf[i++] = 0x00; @@ -826,6 +860,9 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->bulk = 1; + sd->gain = GAIN_DEF; + sd->expo = EXPO_DEF; + return 0; } @@ -1052,8 +1089,7 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev) return; sd->do_ctrl = 0; - setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure), - v4l2_ctrl_g_ctrl(sd->gain)); + setexposure(gspca_dev); gspca_dev->cam.bulk_nurbs = 1; ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC); @@ -1077,55 +1113,48 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { - 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; + sd->gain = val; + if (gspca_dev->streaming) + sd->do_ctrl = 1; + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, ctrl->val, sd->gain->val); - break; - } - return gspca_dev->usb_err; + *val = sd->gain; + return 0; } +static int sd_setexpo(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, -}; + sd->expo = val; + if (gspca_dev->streaming) + sd->do_ctrl = 1; + return 0; +} -static int sd_init_controls(struct gspca_dev *gspca_dev) +static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val) { - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 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); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - v4l2_ctrl_cluster(2, &sd->exposure); + *val = sd->expo; 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, @@ -1165,7 +1194,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/stk014.c b/trunk/drivers/media/video/gspca/stk014.c index 8c0982607f25..4ae7cc8f463a 100644 --- a/trunk/drivers/media/video/gspca/stk014.c +++ b/trunk/drivers/media/video/gspca/stk014.c @@ -29,14 +29,86 @@ MODULE_AUTHOR("Jean-Francois Moine "); MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver"); MODULE_LICENSE("GPL"); -#define QUALITY 50 +/* controls */ +enum e_ctrl { + BRIGHTNESS, + CONTRAST, + COLORS, + 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]; + + 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, @@ -183,36 +255,41 @@ static void set_par(struct gspca_dev *gspca_dev, snd_val(gspca_dev, 0x003f08, parval); } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int parval; parval = 0x06000000 /* whiteness */ - + (val << 16); + + (sd->ctrls[BRIGHTNESS].val << 16); set_par(gspca_dev, parval); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int parval; parval = 0x07000000 /* contrast */ - + (val << 16); + + (sd->ctrls[CONTRAST].val << 16); set_par(gspca_dev, parval); } -static void setcolors(struct gspca_dev *gspca_dev, s32 val) +static void setcolors(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int parval; parval = 0x08000000 /* saturation */ - + (val << 16); + + (sd->ctrls[COLORS].val << 16); set_par(gspca_dev, parval); } -static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) +static void setlightfreq(struct gspca_dev *gspca_dev) { - set_par(gspca_dev, val == 1 + struct sd *sd = (struct sd *) gspca_dev; + + set_par(gspca_dev, sd->ctrls[LIGHTFREQ].val == 1 ? 0x33640000 /* 50 Hz */ : 0x33780000); /* 60 Hz */ } @@ -221,8 +298,12 @@ static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) 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; } @@ -252,7 +333,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, QUALITY); + jpeg_set_qual(sd->jpeg_hdr, sd->quality); /* work on alternate 1 */ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); @@ -284,10 +365,14 @@ 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); @@ -350,70 +435,62 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) { - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - - gspca_dev->usb_err = 0; + static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; - 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; + switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: - setlightfreq(gspca_dev, ctrl->val); - break; + if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm)) + break; + strcpy((char *) menu->name, freq_nm[menu->index]); + return 0; } - return gspca_dev->usb_err; + return -EINVAL; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; -static int sd_init_controls(struct gspca_dev *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 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; - } + 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; 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 -- */ @@ -439,7 +516,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/stv0680.c b/trunk/drivers/media/video/gspca/stv0680.c index 67605272aaa8..461ed645f309 100644 --- a/trunk/drivers/media/video/gspca/stv0680.c +++ b/trunk/drivers/media/video/gspca/stv0680.c @@ -46,6 +46,10 @@ 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) { @@ -314,6 +318,8 @@ 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, @@ -346,7 +352,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/sunplus.c b/trunk/drivers/media/video/gspca/sunplus.c index 9ccfcb1c6479..c80f0c0c75b6 100644 --- a/trunk/drivers/media/video/gspca/sunplus.c +++ b/trunk/drivers/media/video/gspca/sunplus.c @@ -30,13 +30,18 @@ MODULE_AUTHOR("Michel Xhaard "); 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 */ - bool autogain; + s8 brightness; + u8 contrast; + u8 colors; + u8 autogain; + u8 quality; +#define QUALITY_MIN 70 +#define QUALITY_MAX 95 +#define QUALITY_DEF 85 u8 bridge; #define BRIDGE_SPCA504 0 @@ -54,6 +59,75 @@ 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, @@ -523,31 +597,31 @@ static void spca504B_setQtable(struct gspca_dev *gspca_dev) spca504B_PollingDataReady(gspca_dev); } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u16 reg; reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7; - reg_w_riv(gspca_dev, 0x00, reg, val); + reg_w_riv(gspca_dev, 0x00, reg, sd->brightness); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 val) +static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u16 reg; reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8; - reg_w_riv(gspca_dev, 0x00, reg, val); + reg_w_riv(gspca_dev, 0x00, reg, sd->contrast); } -static void setcolors(struct gspca_dev *gspca_dev, s32 val) +static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u16 reg; reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae; - reg_w_riv(gspca_dev, 0x00, reg, val); + reg_w_riv(gspca_dev, 0x00, reg, sd->colors); } static void init_ctl_reg(struct gspca_dev *gspca_dev) @@ -555,6 +629,10 @@ 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: @@ -626,6 +704,11 @@ 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; } @@ -724,7 +807,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, QUALITY); + jpeg_set_qual(sd->jpeg_hdr, sd->quality); if (sd->bridge == BRIDGE_SPCA504B) spca504B_setQtable(gspca_dev); @@ -929,69 +1012,116 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; + struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_err = 0; + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return gspca_dev->usb_err; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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; - } + *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 gspca_dev->usb_err; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; -static int sd_init_controls(struct gspca_dev *gspca_dev) + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) { - 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, -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); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + 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; 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 -- */ @@ -1078,7 +1208,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/t613.c b/trunk/drivers/media/video/gspca/t613.c index 8bc6c3ceec2c..9b9f85a8e60e 100644 --- a/trunk/drivers/media/video/gspca/t613.c +++ b/trunk/drivers/media/video/gspca/t613.c @@ -34,19 +34,28 @@ #include #include "gspca.h" +#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0) + MODULE_AUTHOR("Leandro Costantino "); MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver"); MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_ctrl *freq; - struct { /* awb / color gains control cluster */ - struct v4l2_ctrl *awb; - struct v4l2_ctrl *gain; - struct v4l2_ctrl *red_balance; - struct v4l2_ctrl *blue_balance; - }; + + u8 brightness; + u8 contrast; + u8 colors; + u8 autogain; + u8 gamma; + u8 sharpness; + u8 freq; + u8 red_gain; + u8 blue_gain; + u8 green_gain; + u8 awb; /* set default r/g/b and activate */ + u8 mirror; + u8 effect; u8 sensor; u8 button_pressed; @@ -58,31 +67,245 @@ enum sensors { SENSOR_LT168G, /* must verify if this is the actual model */ }; +/* 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_setlowlight(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgamma(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 int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); + +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getred_gain(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_setmirror(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val); +static int sd_geteffect(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 = 14, + .step = 1, +#define BRIGHTNESS_DEF 8 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0x0d, + .step = 1, +#define CONTRAST_DEF 0x07 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 0x0f, + .step = 1, +#define COLORS_DEF 0x05 + .default_value = COLORS_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define GAMMA_MAX 16 +#define GAMMA_DEF 10 + { + { + .id = V4L2_CID_GAMMA, /* (gamma on win) */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = GAMMA_MAX - 1, + .step = 1, + .default_value = GAMMA_DEF, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, + { + { + .id = V4L2_CID_BACKLIGHT_COMPENSATION, /* Activa lowlight, + * some apps dont bring up the + * backligth_compensation control) */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Low Light", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 0x01 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setlowlight, + .get = sd_getlowlight, + }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror Image", + .minimum = 0, + .maximum = 1, + .step = 1, +#define MIRROR_DEF 0 + .default_value = MIRROR_DEF, + }, + .set = sd_setmirror, + .get = sd_getmirror + }, + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light Frequency Filter", + .minimum = 1, /* 1 -> 0x50, 2->0x60 */ + .maximum = 2, + .step = 1, +#define FREQ_DEF 1 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq}, + + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AWB_DEF 0 + .default_value = AWB_DEF, + }, + .set = sd_setawb, + .get = sd_getawb + }, + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 15, + .step = 1, +#define SHARPNESS_DEF 0x06 + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, + { + { + .id = V4L2_CID_EFFECTS, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Webcam Effects", + .minimum = 0, + .maximum = 4, + .step = 1, +#define EFFECTS_DEF 0 + .default_value = EFFECTS_DEF, + }, + .set = sd_seteffect, + .get = sd_geteffect + }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define BLUE_GAIN_DEF 0x20 + .default_value = BLUE_GAIN_DEF, + }, + .set = sd_setblue_gain, + .get = sd_getblue_gain, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define RED_GAIN_DEF 0x20 + .default_value = RED_GAIN_DEF, + }, + .set = sd_setred_gain, + .get = sd_getred_gain, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define GAIN_DEF 0x20 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +}; + static const struct v4l2_pix_format vga_mode_t16[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 4}, -#if 0 /* HDG: broken with my test cam, so lets disable it */ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 3}, -#endif {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, -#if 0 /* HDG: broken with my test cam, so lets disable it */ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 352, .sizeimage = 352 * 288 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, -#endif {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, @@ -231,6 +454,17 @@ static const struct additional_sensor_data sensor_data[] = { }; #define MAX_EFFECTS 7 +/* easily done by soft, this table could be removed, + * i keep it here just in case */ +static char *effects_control[MAX_EFFECTS] = { + "Normal", + "Emboss", /* disabled */ + "Monochrome", + "Sepia", + "Sketch", + "Sun Effect", /* disabled */ + "Negative", +}; static const u8 effects_table[MAX_EFFECTS][6] = { {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ @@ -241,8 +475,7 @@ static const u8 effects_table[MAX_EFFECTS][6] = { {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */ }; -#define GAMMA_MAX (15) -static const u8 gamma_table[GAMMA_MAX+1][17] = { +static const u8 gamma_table[GAMMA_MAX][17] = { /* gamma table from cam1690.ini */ {0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21, /* 0 */ 0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb, @@ -450,18 +683,38 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev) static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { - struct cam *cam = &gspca_dev->cam; + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; cam->cam_mode = vga_mode_t16; cam->nmodes = ARRAY_SIZE(vga_mode_t16); + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLORS_DEF; + sd->gamma = GAMMA_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->mirror = MIRROR_DEF; + sd->freq = FREQ_DEF; + sd->awb = AWB_DEF; + sd->sharpness = SHARPNESS_DEF; + sd->effect = EFFECTS_DEF; + sd->red_gain = RED_GAIN_DEF; + sd->blue_gain = BLUE_GAIN_DEF; + sd->green_gain = GAIN_DEF * 3 - RED_GAIN_DEF - BLUE_GAIN_DEF; + return 0; } -static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) +static void setbrightness(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + unsigned int brightness; u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 }; + brightness = sd->brightness; if (brightness < 7) { set6[1] = 0x26; set6[3] = 0x70 - brightness * 0x10; @@ -472,8 +725,10 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness) reg_w_buf(gspca_dev, set6, sizeof set6); } -static void setcontrast(struct gspca_dev *gspca_dev, s32 contrast) +static void setcontrast(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + unsigned int contrast = sd->contrast; u16 reg_to_write; if (contrast < 7) @@ -484,62 +739,89 @@ static void setcontrast(struct gspca_dev *gspca_dev, s32 contrast) reg_w(gspca_dev, reg_to_write); } -static void setcolors(struct gspca_dev *gspca_dev, s32 val) +static void setcolors(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u16 reg_to_write; - reg_to_write = 0x80bb + val * 0x100; /* was 0xc0 */ + reg_to_write = 0x80bb + sd->colors * 0x100; /* was 0xc0 */ reg_w(gspca_dev, reg_to_write); } -static void setgamma(struct gspca_dev *gspca_dev, s32 val) +static void setgamma(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + PDEBUG(D_CONF, "Gamma: %d", sd->gamma); reg_w_ixbuf(gspca_dev, 0x90, - gamma_table[val], sizeof gamma_table[0]); + gamma_table[sd->gamma], sizeof gamma_table[0]); } -static void setawb_n_RGB(struct gspca_dev *gspca_dev) +static void setRGB(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 all_gain_reg[8] = { - 0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00 }; - s32 red_gain, blue_gain, green_gain; - - green_gain = sd->gain->val; - - red_gain = green_gain + sd->red_balance->val; - if (red_gain > 0x40) - red_gain = 0x40; - else if (red_gain < 0x10) - red_gain = 0x10; - - blue_gain = green_gain + sd->blue_balance->val; - if (blue_gain > 0x40) - blue_gain = 0x40; - else if (blue_gain < 0x10) - blue_gain = 0x10; - - all_gain_reg[1] = red_gain; - all_gain_reg[3] = blue_gain; - all_gain_reg[5] = green_gain; - all_gain_reg[7] = sensor_data[sd->sensor].reg80; - if (!sd->awb->val) - all_gain_reg[7] &= ~0x04; /* AWB off */ + u8 all_gain_reg[6] = + {0x87, 0x00, 0x88, 0x00, 0x89, 0x00}; + all_gain_reg[1] = sd->red_gain; + all_gain_reg[3] = sd->blue_gain; + all_gain_reg[5] = sd->green_gain; reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg); } -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +/* Generic fnc for r/b balance, exposure and awb */ +static void setawb(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + u16 reg80; + + reg80 = (sensor_data[sd->sensor].reg80 << 8) | 0x80; + + /* on awb leave defaults values */ + if (!sd->awb) { + /* shoud we wait here.. */ + /* update and reset RGB gains with webcam values */ + sd->red_gain = reg_r(gspca_dev, 0x0087); + sd->blue_gain = reg_r(gspca_dev, 0x0088); + sd->green_gain = reg_r(gspca_dev, 0x0089); + reg80 &= ~0x0400; /* AWB off */ + } + reg_w(gspca_dev, reg80); + reg_w(gspca_dev, reg80); +} + +static void init_gains(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 reg80; + u8 all_gain_reg[8] = + {0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00}; + + all_gain_reg[1] = sd->red_gain; + all_gain_reg[3] = sd->blue_gain; + all_gain_reg[5] = sd->green_gain; + reg80 = sensor_data[sd->sensor].reg80; + if (!sd->awb) + reg80 &= ~0x04; + all_gain_reg[7] = reg80; + reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg); + + reg_w(gspca_dev, (sd->red_gain << 8) + 0x87); + reg_w(gspca_dev, (sd->blue_gain << 8) + 0x88); + reg_w(gspca_dev, (sd->green_gain << 8) + 0x89); +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; u16 reg_to_write; - reg_to_write = 0x0aa6 + 0x1000 * val; + reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness; reg_w(gspca_dev, reg_to_write); } -static void setfreq(struct gspca_dev *gspca_dev, s32 val) +static void setfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 reg66; @@ -547,7 +829,7 @@ static void setfreq(struct gspca_dev *gspca_dev, s32 val) switch (sd->sensor) { case SENSOR_LT168G: - if (val != 0) + if (sd->freq != 0) freq[3] = 0xa8; reg66 = 0x41; break; @@ -558,7 +840,7 @@ static void setfreq(struct gspca_dev *gspca_dev, s32 val) reg66 = 0x40; break; } - switch (val) { + switch (sd->freq) { case 0: /* no flicker */ freq[3] = 0xf0; break; @@ -659,9 +941,14 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e); - reg_w(gspca_dev, (0x20 << 8) + 0x87); - reg_w(gspca_dev, (0x20 << 8) + 0x88); - reg_w(gspca_dev, (0x20 << 8) + 0x89); + + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setgamma(gspca_dev); + setcolors(gspca_dev); + setsharpness(gspca_dev); + init_gains(gspca_dev); + setfreq(gspca_dev); reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5); reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8); @@ -681,44 +968,31 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void setmirror(struct gspca_dev *gspca_dev, s32 val) +static void setmirror(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 hflipcmd[8] = {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}; - if (val) + if (sd->mirror) hflipcmd[3] = 0x01; reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd); } -static void seteffect(struct gspca_dev *gspca_dev, s32 val) +static void seteffect(struct gspca_dev *gspca_dev) { - int idx = 0; - - switch (val) { - case V4L2_COLORFX_NONE: - break; - case V4L2_COLORFX_BW: - idx = 2; - break; - case V4L2_COLORFX_SEPIA: - idx = 3; - break; - case V4L2_COLORFX_SKETCH: - idx = 4; - break; - case V4L2_COLORFX_NEGATIVE: - idx = 6; - break; - default: - break; - } + struct sd *sd = (struct sd *) gspca_dev; - reg_w_buf(gspca_dev, effects_table[idx], + reg_w_buf(gspca_dev, effects_table[sd->effect], sizeof effects_table[0]); + if (sd->effect == 1 || sd->effect == 5) { + PDEBUG(D_CONF, + "This effect have been disabled for webcam \"safety\""); + return; + } - if (val == V4L2_COLORFX_SKETCH) + if (sd->effect == 1 || sd->effect == 4) reg_w(gspca_dev, 0x4aa6); else reg_w(gspca_dev, 0xfaa6); @@ -796,7 +1070,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } sensor = &sensor_data[sd->sensor]; - setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); + setfreq(gspca_dev); reg_r(gspca_dev, 0x0012); reg_w_buf(gspca_dev, t2, sizeof t2); reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3); @@ -868,157 +1142,296 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, pkt_type, data, len); } -static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val) { - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; - s32 red_gain, blue_gain, green_gain; - - gspca_dev->usb_err = 0; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - red_gain = reg_r(gspca_dev, 0x0087); - if (red_gain > 0x40) - red_gain = 0x40; - else if (red_gain < 0x10) - red_gain = 0x10; - - blue_gain = reg_r(gspca_dev, 0x0088); - if (blue_gain > 0x40) - blue_gain = 0x40; - else if (blue_gain < 0x10) - blue_gain = 0x10; - - green_gain = reg_r(gspca_dev, 0x0089); - if (green_gain > 0x40) - green_gain = 0x40; - else if (green_gain < 0x10) - green_gain = 0x10; - - sd->gain->val = green_gain; - sd->red_balance->val = red_gain - green_gain; - sd->blue_balance->val = blue_gain - green_gain; - break; - } + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue_gain = val; + if (gspca_dev->streaming) + reg_w(gspca_dev, (val << 8) + 0x88); return 0; } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val) { - 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; + *val = sd->blue_gain; + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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_GAMMA: - setgamma(gspca_dev, ctrl->val); - break; - case V4L2_CID_HFLIP: - setmirror(gspca_dev, ctrl->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setfreq(gspca_dev, ctrl->val); - break; - case V4L2_CID_BACKLIGHT_COMPENSATION: - reg_w(gspca_dev, ctrl->val ? 0xf48e : 0xb48e); - break; - case V4L2_CID_AUTO_WHITE_BALANCE: - setawb_n_RGB(gspca_dev); - break; - case V4L2_CID_COLORFX: - seteffect(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; + sd->red_gain = val; + if (gspca_dev->streaming) + reg_w(gspca_dev, (val << 8) + 0x87); + + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .g_volatile_ctrl = sd_g_volatile_ctrl, - .s_ctrl = sd_s_ctrl, -}; +static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; -static int sd_init_controls(struct gspca_dev *gspca_dev) + *val = sd->red_gain; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { - 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, 12); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 14, 1, 8); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0x0d, 1, 7); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 0, 0xf, 1, 5); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 10); - /* Activate lowlight, some apps dont bring up the - backlight_compensation control) */ - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BACKLIGHT_COMPENSATION, 0, 1, 1, 1); - if (sd->sensor == SENSOR_TAS5130A) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sd->awb = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0x10, 0x40, 1, 0x20); - sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, 0); - sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, 0); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 15, 1, 6); - v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, - V4L2_CID_COLORFX, V4L2_COLORFX_SKETCH, - ~((1 << V4L2_COLORFX_NONE) | - (1 << V4L2_COLORFX_BW) | - (1 << V4L2_COLORFX_SEPIA) | - (1 << V4L2_COLORFX_SKETCH) | - (1 << V4L2_COLORFX_NEGATIVE)), - V4L2_COLORFX_NONE); - 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_50HZ); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + struct sd *sd = (struct sd *) gspca_dev; + u16 psg, nsg; + + psg = sd->red_gain + sd->blue_gain + sd->green_gain; + nsg = val * 3; + sd->red_gain = sd->red_gain * nsg / psg; + if (sd->red_gain > 0x40) + sd->red_gain = 0x40; + else if (sd->red_gain < 0x10) + sd->red_gain = 0x10; + sd->blue_gain = sd->blue_gain * nsg / psg; + if (sd->blue_gain > 0x40) + sd->blue_gain = 0x40; + else if (sd->blue_gain < 0x10) + sd->blue_gain = 0x10; + sd->green_gain = sd->green_gain * nsg / psg; + if (sd->green_gain > 0x40) + sd->green_gain = 0x40; + else if (sd->green_gain < 0x10) + sd->green_gain = 0x10; + + if (gspca_dev->streaming) + setRGB(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = (sd->red_gain + sd->blue_gain + sd->green_gain) / 3; + return 0; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(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 *val; +} + +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->awb = val; + if (gspca_dev->streaming) + setawb(gspca_dev); + return 0; +} + +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - v4l2_ctrl_auto_cluster(4, &sd->awb, 0, true); + *val = sd->awb; + return *val; +} + +static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirror = val; + if (gspca_dev->streaming) + setmirror(gspca_dev); + return 0; +} + +static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->mirror; + return *val; +} + +static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->effect = val; + if (gspca_dev->streaming) + seteffect(gspca_dev); + return 0; +} + +static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->effect; + return *val; +} + +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 *val; +} + +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_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + setgamma(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gamma; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} +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_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 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +/* Low Light set here......*/ +static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val != 0) + reg_w(gspca_dev, 0xf48e); + else + reg_w(gspca_dev, 0xb48e); + return 0; +} + +static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; + + 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]); + return 0; + case V4L2_CID_EFFECTS: + if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) { + strlcpy((char *) menu->name, + effects_control[menu->index], + sizeof menu->name); + return 0; + } + break; + } + return -EINVAL; +} + /* 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, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) .other_input = 1, #endif @@ -1047,7 +1460,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/topro.c b/trunk/drivers/media/video/gspca/topro.c index a6055246cb9d..c6326d177a3d 100644 --- a/trunk/drivers/media/video/gspca/topro.c +++ b/trunk/drivers/media/video/gspca/topro.c @@ -120,13 +120,24 @@ 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 v4l2_ctrl *jpegqual; - struct v4l2_ctrl *sharpness; - struct v4l2_ctrl *gamma; - struct v4l2_ctrl *blue; - struct v4l2_ctrl *red; + + struct gspca_ctrl ctrls[NCTRLS]; u8 framerate; u8 quality; /* webcam current JPEG quality (0..16) */ @@ -1404,33 +1415,32 @@ static void soi763a_6810_init(struct gspca_dev *gspca_dev) } /* set the gain and exposure */ -static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain, - s32 blue, s32 red) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; if (sd->sensor == SENSOR_CX0342) { - expo = (expo << 2) - 1; + int expo; + + expo = (sd->ctrls[EXPOSURE].val << 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, - gain >> 8); - i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain); + sd->ctrls[GAIN].val >> 8); + i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val); if (sd->bridge == BRIDGE_TP6800) i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H, - 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); - } + 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); i2c_w(gspca_dev, CX0342_SYS_CTRL_0, sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81); return; @@ -1438,10 +1448,10 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain, /* soi763a */ i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */ - expo); + sd->ctrls[EXPOSURE].val); /* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */ i2c_w(gspca_dev, 0x00, /* gain */ - gain); + sd->ctrls[GAIN].val); } /* set the JPEG quantization tables */ @@ -1462,10 +1472,12 @@ 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, s32 q) +static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u16 q; + q = sd->ctrls[QUALITY].val; if (q != 16) q = 15 - q; @@ -1496,9 +1508,10 @@ 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, s32 gamma) +static void setgamma(struct gspca_dev *gspca_dev) { 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 */ @@ -3823,6 +3836,7 @@ static void setgamma(struct gspca_dev *gspca_dev, s32 gamma) 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); @@ -3850,35 +3864,43 @@ static void setgamma(struct gspca_dev *gspca_dev, s32 gamma) /* msleep(50); */ } -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 val; if (sd->bridge == BRIDGE_TP6800) { - val |= 0x08; /* grid compensation enable */ + val = sd->ctrls[SHARPNESS].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 = (val << 5) | 0x08; + val = (sd->ctrls[SHARPNESS].val << 5) | 0x08; reg_w(gspca_dev, 0x59, val); } } -static void setautogain(struct gspca_dev *gspca_dev, s32 val) +static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sd->ag_cnt = val ? AG_CNT_START : -1; + 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)); + } } /* 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); @@ -3904,9 +3926,8 @@ 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, v4l2_ctrl_g_ctrl(sd->gamma)); - if (sd->sensor == SENSOR_SOI763A) - setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); + setgamma(gspca_dev); + setquality(gspca_dev); } /* convert the frame rate to a tp68x0 value */ @@ -3942,7 +3963,7 @@ static int get_fr_idx(struct gspca_dev *gspca_dev) return i; } -static void setframerate(struct gspca_dev *gspca_dev, s32 val) +static void setframerate(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 fr_idx; @@ -3953,7 +3974,7 @@ static void setframerate(struct gspca_dev *gspca_dev, s32 val) reg_r(gspca_dev, 0x7b); reg_w(gspca_dev, 0x7b, sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90); - if (val >= 128) + if (sd->ctrls[EXPOSURE].val >= 128) fr_idx = 0xf0; /* lower frame rate */ } @@ -3963,43 +3984,43 @@ static void setframerate(struct gspca_dev *gspca_dev, s32 val) i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); } -static void setrgain(struct gspca_dev *gspca_dev, s32 rgain) +static void setrgain(struct gspca_dev *gspca_dev) { + 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) +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - s32 val = gspca_dev->gain->val; if (sd->sensor == SENSOR_CX0342) { - 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[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; } + 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, s32 bgain) +static void setbgain(struct gspca_dev *gspca_dev) { + 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); @@ -4019,6 +4040,7 @@ 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; } @@ -4086,16 +4108,32 @@ 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; } @@ -4169,9 +4207,8 @@ 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 useful? */ +/*fixme: is this usefull?*/ {TP6800_R17_GPIO_IO, 0x9f}, {TP6800_R16_GPIO_PD, 0x40}, {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */ @@ -4242,21 +4279,13 @@ 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); - 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); + setexposure(gspca_dev); 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}, @@ -4337,10 +4366,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, v4l2_ctrl_g_ctrl(sd->gamma)); + setgamma(gspca_dev); reg_w_buf(gspca_dev, tp6810_bridge_start, ARRAY_SIZE(tp6810_bridge_start)); - setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); + setsharpness(gspca_dev); bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342], ARRAY_SIZE(color_gain[0])); reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87); @@ -4351,12 +4380,11 @@ 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, v4l2_ctrl_g_ctrl(sd->jpegqual)); */ +/* setquality(gspca_dev); */ } 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}, @@ -4456,28 +4484,19 @@ 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, v4l2_ctrl_g_ctrl(sd->sharpness)); + setsharpness(gspca_dev); bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A], ARRAY_SIZE(color_gain[0])); set_led(gspca_dev, 1); - 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)); + setexposure(gspca_dev); + setquality(gspca_dev); + setgamma(gspca_dev); } 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}, @@ -4501,14 +4520,7 @@ 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); - 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); + setexposure(gspca_dev); 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)); @@ -4522,7 +4534,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, v4l2_ctrl_g_ctrl(sd->gamma)); + setgamma(gspca_dev); reg_w_buf(gspca_dev, tp6810_bridge_start, ARRAY_SIZE(tp6810_bridge_start)); @@ -4533,19 +4545,12 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x00, 0x00); - setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); + setsharpness(gspca_dev); 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); - 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); + setexposure(gspca_dev); reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6)); } @@ -4571,25 +4576,12 @@ 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); - 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)); + setexposure(gspca_dev); + setquality(gspca_dev); + setautogain(gspca_dev); } - setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); + setframerate(gspca_dev); return gspca_dev->usb_err; } @@ -4680,6 +4672,12 @@ 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; @@ -4741,19 +4739,17 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev) luma /= 4; reg_w(gspca_dev, 0x7d, 0x00); - expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); - ret = gspca_expo_autogain(gspca_dev, luma, + expo = sd->ctrls[EXPOSURE].val; + ret = auto_gain_n_exposure(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) { - 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); + if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128) + || (expo < 128 && sd->ctrls[EXPOSURE].val >= 128)) + setframerate(gspca_dev); } break; } @@ -4793,7 +4789,7 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev, sd->framerate = tpf->denominator / tpf->numerator; if (gspca_dev->streaming) - setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); + setframerate(gspca_dev); /* Return the actual framerate */ i = get_fr_idx(gspca_dev); @@ -4810,10 +4806,12 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; - if (sd->sensor != SENSOR_SOI763A) - return -ENOTTY; - v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); - return 0; + if (sd->sensor == SENSOR_SOI763A) + jpeg_set_qual(sd->jpeg_hdr, jcomp->quality); +/* else + fixme: TODO +*/ + return gspca_dev->usb_err; } static int sd_get_jcomp(struct gspca_dev *gspca_dev, @@ -4821,109 +4819,118 @@ 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 = v4l2_ctrl_g_ctrl(sd->jpegqual); + jcomp->quality = jpeg_q[sd->quality]; jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; 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_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 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_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, diff --git a/trunk/drivers/media/video/gspca/tv8532.c b/trunk/drivers/media/video/gspca/tv8532.c index 8591324a53e1..c8922c5ffbf5 100644 --- a/trunk/drivers/media/video/gspca/tv8532.c +++ b/trunk/drivers/media/video/gspca/tv8532.c @@ -30,9 +30,49 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + __u16 exposure; + __u16 gain; + __u8 packet; }; +/* V4L2 controls supported by the driver */ +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_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 = 1, + .maximum = 0x18f, + .step = 1, +#define EXPOSURE_DEF 0x18f + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 0x7ff, + .step = 1, +#define GAIN_DEF 0x100 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +}; + static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 176, @@ -162,12 +202,15 @@ static void tv_8532WriteEEprom(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 = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; return 0; } @@ -198,19 +241,23 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void setexposure(struct gspca_dev *gspca_dev, s32 val) +static void setexposure(struct gspca_dev *gspca_dev) { - reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->exposure); reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); /* 0x84 */ } -static void setgain(struct gspca_dev *gspca_dev, s32 val) +static void setgain(struct gspca_dev *gspca_dev) { - reg_w2(gspca_dev, R20_GAIN_G1L, val); - reg_w2(gspca_dev, R22_GAIN_RL, val); - reg_w2(gspca_dev, R24_GAIN_BL, val); - reg_w2(gspca_dev, R26_GAIN_G2L, val); + struct sd *sd = (struct sd *) gspca_dev; + + reg_w2(gspca_dev, R20_GAIN_G1L, sd->gain); + reg_w2(gspca_dev, R22_GAIN_RL, sd->gain); + reg_w2(gspca_dev, R24_GAIN_BL, sd->gain); + reg_w2(gspca_dev, R26_GAIN_G2L, sd->gain); } /* -- start the camera -- */ @@ -242,6 +289,9 @@ static int sd_start(struct gspca_dev *gspca_dev) tv_8532_setReg(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); + /************************************************/ reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ msleep(200); @@ -289,55 +339,49 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data + gspca_dev->width + 5, gspca_dev->width); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) { - 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; + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} - if (!gspca_dev->streaming) - return 0; +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAIN: - setgain(gspca_dev, ctrl->val); - break; - } - return gspca_dev->usb_err; + *val = sd->exposure; + return 0; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; -static int sd_init_controls(struct gspca_dev *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 v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 2); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 0x18f, 1, 0x18f); - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 0x7ff, 1, 0x100); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; 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, @@ -371,7 +415,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/vc032x.c b/trunk/drivers/media/video/gspca/vc032x.c index f21fd1677c38..208f6b2d512a 100644 --- a/trunk/drivers/media/video/gspca/vc032x.c +++ b/trunk/drivers/media/video/gspca/vc032x.c @@ -33,10 +33,18 @@ MODULE_LICENSE("GPL"); /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct { /* hvflip cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; + + u8 brightness; + u8 contrast; + u8 colors; + u8 hflip; + u8 vflip; + u8 lightfreq; + s8 sharpness; + u16 exposure; + u8 gain; + u8 autogain; + u8 backlight; u8 image_offset; @@ -65,6 +73,252 @@ enum sensors { NSENSORS }; +/* 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_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(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_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(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_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(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 int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbacklight(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 = 255, + .step = 1, +#define BRIGHTNESS_DEF 128 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define CONTRAST_IDX 1 + { + { + .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 COLORS_IDX 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 1, + .maximum = 127, + .step = 1, +#define COLOR_DEF 63 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +/* next 2 controls work with some sensors only */ +#define HFLIP_IDX 3 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, +#define VFLIP_IDX 4 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +#define LIGHTFREQ_IDX 5 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: No, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 1 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +#define SHARPNESS_IDX 6 + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = -1, + .maximum = 2, + .step = 1, +#define SHARPNESS_DEF -1 + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, +#define GAIN_IDX 7 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 78, + .step = 1, +#define GAIN_DEF 0 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +#define EXPOSURE_IDX 8 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", +#define EXPOSURE_DEF 450 + .minimum = 0, + .maximum = 4095, + .step = 1, + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, +#define AUTOGAIN_IDX 9 + { + { + .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, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define BACKLIGHT_IDX 10 + { + { + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Backlight Compensation", + .minimum = 0, + .maximum = 15, + .step = 1, +#define BACKLIGHT_DEF 15 + .default_value = BACKLIGHT_DEF, + }, + .set = sd_setbacklight, + .get = sd_getbacklight, + }, +}; + +/* table of the disabled controls */ +static u32 ctrl_dis[NSENSORS] = { + [SENSOR_HV7131R] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) + | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_MI0360] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) + | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_MI1310_SOC] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_MI1320] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_MI1320_SOC] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_OV7660] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_OV7670] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_PO1200] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_PO3130NC] = + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) + | (1 << SHARPNESS_IDX) + | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX) + | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX), + [SENSOR_POxxxx] = + (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX), +}; static const struct v4l2_pix_format vc0321_mode[] = { {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, @@ -3151,6 +3405,18 @@ static int sd_config(struct gspca_dev *gspca_dev, (id->idProduct == 0x0892 || id->idProduct == 0x0896)) sd->sensor = SENSOR_POxxxx; /* no probe */ + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; + sd->lightfreq = FREQ_DEF; + sd->sharpness = SHARPNESS_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPOSURE_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->backlight = BACKLIGHT_DEF; + return 0; } @@ -3246,6 +3512,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } } cam->npkt = npkt[sd->sensor]; + gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; if (sd->sensor == SENSOR_OV7670) sd->flags |= FL_HFLIP | FL_VFLIP; @@ -3267,11 +3534,14 @@ static int sd_init(struct gspca_dev *gspca_dev) return gspca_dev->usb_err; } -static void setbrightness(struct gspca_dev *gspca_dev, s32 val) +static void setbrightness(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 data; - data = val; + if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX)) + return; + data = sd->brightness; if (data >= 0x80) data &= 0x7f; else @@ -3279,27 +3549,36 @@ static void setbrightness(struct gspca_dev *gspca_dev, s32 val) i2c_write(gspca_dev, 0x98, &data, 1); } -static void setcontrast(struct gspca_dev *gspca_dev, u8 val) +static void setcontrast(struct gspca_dev *gspca_dev) { - i2c_write(gspca_dev, 0x99, &val, 1); + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX)) + return; + i2c_write(gspca_dev, 0x99, &sd->contrast, 1); } -static void setcolors(struct gspca_dev *gspca_dev, u8 val) +static void setcolors(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 data; - data = val - (val >> 3) - 1; + if (gspca_dev->ctrl_dis & (1 << COLORS_IDX)) + return; + data = sd->colors - (sd->colors >> 3) - 1; i2c_write(gspca_dev, 0x94, &data, 1); - i2c_write(gspca_dev, 0x95, &val, 1); + i2c_write(gspca_dev, 0x95, &sd->colors, 1); } -static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip) +static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 data[2]; + u8 data[2], hflip, vflip; + hflip = sd->hflip; if (sd->flags & FL_HFLIP) hflip = !hflip; + vflip = sd->vflip; if (sd->flags & FL_VFLIP) vflip = !vflip; switch (sd->sensor) { @@ -3331,7 +3610,7 @@ static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip) } } -static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) +static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; static const u8 (*ov7660_freq_tb[3])[4] = @@ -3339,10 +3618,10 @@ static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) if (sd->sensor != SENSOR_OV7660) return; - usb_exchange(gspca_dev, ov7660_freq_tb[val]); + usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]); } -static void setsharpness(struct gspca_dev *gspca_dev, s32 val) +static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 data; @@ -3351,41 +3630,51 @@ static void setsharpness(struct gspca_dev *gspca_dev, s32 val) case SENSOR_PO1200: data = 0; i2c_write(gspca_dev, 0x03, &data, 1); - if (val < 0) + if (sd->sharpness < 0) data = 0x6a; else - data = 0xb5 + val * 3; + data = 0xb5 + sd->sharpness * 3; i2c_write(gspca_dev, 0x61, &data, 1); break; case SENSOR_POxxxx: - if (val < 0) + if (sd->sharpness < 0) data = 0x7e; /* def = max */ else - data = 0x60 + val * 0x0f; + data = 0x60 + sd->sharpness * 0x0f; i2c_write(gspca_dev, 0x59, &data, 1); break; } } -static void setgain(struct gspca_dev *gspca_dev, u8 val) +static void setgain(struct gspca_dev *gspca_dev) { - i2c_write(gspca_dev, 0x15, &val, 1); + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << GAIN_IDX)) + return; + i2c_write(gspca_dev, 0x15, &sd->gain, 1); } -static void setexposure(struct gspca_dev *gspca_dev, s32 val) +static void setexposure(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u8 data; - data = val >> 8; + if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX)) + return; + data = sd->exposure >> 8; i2c_write(gspca_dev, 0x1a, &data, 1); - data = val; + data = sd->exposure; i2c_write(gspca_dev, 0x1b, &data, 1); } -static void setautogain(struct gspca_dev *gspca_dev, s32 val) +static void setautogain(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; static const u8 data[2] = {0x28, 0x3c}; - i2c_write(gspca_dev, 0xd1, &data[val], 1); + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) + return; + i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1); } static void setgamma(struct gspca_dev *gspca_dev) @@ -3394,29 +3683,30 @@ static void setgamma(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, poxxxx_gamma); } -static void setbacklight(struct gspca_dev *gspca_dev, s32 val) +static void setbacklight(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u16 v; u8 data; - data = (val << 4) | 0x0f; + data = (sd->backlight << 4) | 0x0f; i2c_write(gspca_dev, 0xaa, &data, 1); - v = 613 + 12 * val; + v = 613 + 12 * sd->backlight; data = v >> 8; i2c_write(gspca_dev, 0xc4, &data, 1); data = v; i2c_write(gspca_dev, 0xc5, &data, 1); - v = 1093 - 12 * val; + v = 1093 - 12 * sd->backlight; data = v >> 8; i2c_write(gspca_dev, 0xc6, &data, 1); data = v; i2c_write(gspca_dev, 0xc7, &data, 1); - v = 342 + 9 * val; + v = 342 + 9 * sd->backlight; data = v >> 8; i2c_write(gspca_dev, 0xc8, &data, 1); data = v; i2c_write(gspca_dev, 0xc9, &data, 1); - v = 702 - 9 * val; + v = 702 - 9 * sd->backlight; data = v >> 8; i2c_write(gspca_dev, 0xca, &data, 1); data = v; @@ -3543,6 +3833,14 @@ static int sd_start(struct gspca_dev *gspca_dev) /* case SENSOR_POxxxx: */ usb_exchange(gspca_dev, poxxxx_init_common); setgamma(gspca_dev); + setbacklight(gspca_dev); + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); + setsharpness(gspca_dev); + setautogain(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); usb_exchange(gspca_dev, poxxxx_init_start_3); if (mode) init = poxxxx_initQVGA; @@ -3575,6 +3873,8 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } msleep(100); + sethvflip(gspca_dev); + setlightfreq(gspca_dev); } switch (sd->sensor) { case SENSOR_OV7670: @@ -3660,152 +3960,233 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { - struct gspca_dev *gspca_dev = - container_of(ctrl->handler, struct gspca_dev, ctrl_handler); - struct sd *sd = (struct sd *)gspca_dev; + struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_err = 0; + *val = sd->brightness; + return 0; +} - if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY) - return 0; +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; - 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_HFLIP: - sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val); - break; - case V4L2_CID_SHARPNESS: - setsharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_AUTOGAIN: - setautogain(gspca_dev, ctrl->val); - break; - case V4L2_CID_GAIN: - setgain(gspca_dev, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - setexposure(gspca_dev, ctrl->val); - break; - case V4L2_CID_BACKLIGHT_COMPENSATION: - setbacklight(gspca_dev, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - setlightfreq(gspca_dev, ctrl->val); - break; - } + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); return gspca_dev->usb_err; } -static const struct v4l2_ctrl_ops sd_ctrl_ops = { - .s_ctrl = sd_s_ctrl, -}; +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_init_controls(struct gspca_dev *gspca_dev) +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) { - struct sd *sd = (struct sd *)gspca_dev; - struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; - bool has_brightness = false; - bool has_contrast = false; - bool has_sat = false; - bool has_hvflip = false; - bool has_freq = false; - bool has_backlight = false; - bool has_exposure = false; - bool has_autogain = false; - bool has_gain = false; - bool has_sharpness = false; + struct sd *sd = (struct sd *) gspca_dev; - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_MI0360: - case SENSOR_PO3130NC: - break; - case SENSOR_MI1310_SOC: - case SENSOR_MI1320: - case SENSOR_MI1320_SOC: - case SENSOR_OV7660: - has_hvflip = true; - break; - case SENSOR_OV7670: - has_hvflip = has_freq = true; - break; - case SENSOR_PO1200: - has_hvflip = has_sharpness = true; - break; - case SENSOR_POxxxx: - has_brightness = has_contrast = has_sat = has_backlight = - has_exposure = has_autogain = has_gain = - has_sharpness = true; - break; - } + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return gspca_dev->usb_err; +} - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 8); - if (has_brightness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - if (has_contrast) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 127); - if (has_sat) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SATURATION, 1, 127, 1, 63); - if (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 (has_sharpness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, -1, 2, 1, -1); - if (has_freq) - 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_50HZ); - if (has_autogain) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - if (has_gain) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_GAIN, 0, 78, 1, 0); - if (has_exposure) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 4095, 1, 450); - if (has_backlight) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BACKLIGHT_COMPENSATION, 0, 15, 1, 15); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - if (sd->hflip) - v4l2_ctrl_cluster(2, &sd->hflip); +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_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + 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; + 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 gspca_dev->usb_err; +} + +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_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 gspca_dev->usb_err; +} + +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_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 gspca_dev->usb_err; +} + +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_setbacklight(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->backlight = val; + if (gspca_dev->streaming) + setbacklight(gspca_dev); + + return gspca_dev->usb_err; +} + +static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->backlight; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; + + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + if (menu->index >= ARRAY_SIZE(freq_nm)) + break; + strcpy((char *) menu->name, freq_nm[menu->index]); + return 0; + } + return -EINVAL; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .init_controls = sd_init_controls, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, }; /* -- module initialisation -- */ @@ -3846,7 +4227,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/vicam.c b/trunk/drivers/media/video/gspca/vicam.c index b1a64b912666..15a30f7a4b2a 100644 --- a/trunk/drivers/media/video/gspca/vicam.c +++ b/trunk/drivers/media/video/gspca/vicam.c @@ -44,10 +44,17 @@ MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(VICAM_FIRMWARE); +enum e_ctrl { + GAIN, + EXPOSURE, + NCTRL /* number of controls */ +}; + struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ struct work_struct work_struct; struct workqueue_struct *work_thread; + struct gspca_ctrl ctrls[NCTRL]; }; /* The vicam sensor has a resolution of 512 x 244, with I believe square @@ -79,6 +86,31 @@ static struct v4l2_pix_format vicam_mode[] = { .colorspace = V4L2_COLORSPACE_SRGB,}, }; +static const struct ctrl sd_ctrls[] = { +[GAIN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 200, + }, + }, +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 2047, + .step = 1, + .default_value = 256, + }, + }, +}; + static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request, u16 value, u16 index, u8 *data, u16 len) { @@ -114,13 +146,12 @@ static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state) */ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) { + struct sd *sd = (struct sd *)gspca_dev; int ret, unscaled_height, act_len = 0; u8 *req_data = gspca_dev->usb_buf; - s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); - s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); memset(req_data, 0, 16); - req_data[0] = gain; + req_data[0] = sd->ctrls[GAIN].val; if (gspca_dev->width == 256) req_data[1] |= 0x01; /* low nibble x-scale */ if (gspca_dev->height <= 122) { @@ -136,9 +167,9 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) else /* Up to 244 lines with req_data[3] == 0x08 */ req_data[3] = 0x08; /* vend? */ - if (expo < 256) { + if (sd->ctrls[EXPOSURE].val < 256) { /* Frame rate maxed out, use partial frame expo time */ - req_data[4] = 255 - expo; + req_data[4] = 255 - sd->ctrls[EXPOSURE].val; req_data[5] = 0x00; req_data[6] = 0x00; req_data[7] = 0x01; @@ -146,8 +177,8 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) /* Modify frame rate */ req_data[4] = 0x00; req_data[5] = 0x00; - req_data[6] = expo & 0xFF; - req_data[7] = expo >> 8; + req_data[6] = sd->ctrls[EXPOSURE].val & 0xFF; + req_data[7] = sd->ctrls[EXPOSURE].val >> 8; } req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */ /* bytes 9-15 do not seem to affect exposure or image quality */ @@ -229,6 +260,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->bulk_size = 64; cam->cam_mode = vicam_mode; cam->nmodes = ARRAY_SIZE(vicam_mode); + cam->ctrls = sd->ctrls; INIT_WORK(&sd->work_struct, vicam_dostream); @@ -303,24 +335,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) vicam_set_camera_power(gspca_dev, 0); } -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, 2); - gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_EXPOSURE, 0, 2047, 1, 256); - gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_GAIN, 0, 255, 1, 200); - - if (hdl->error) { - pr_err("Could not initialize controls\n"); - return hdl->error; - } - return 0; -} - /* Table of supported USB devices */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x04c1, 0x009d)}, @@ -333,9 +347,10 @@ MODULE_DEVICE_TABLE(usb, device_table); /* 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, }; @@ -358,7 +373,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/gspca/w996Xcf.c b/trunk/drivers/media/video/gspca/w996Xcf.c index 9e3a909e0a00..27d2cef0692a 100644 --- a/trunk/drivers/media/video/gspca/w996Xcf.c +++ b/trunk/drivers/media/video/gspca/w996Xcf.c @@ -404,14 +404,9 @@ static void w9968cf_set_crop_window(struct sd *sd) } if (sd->sensor == SEN_OV7620) { - /* - * Sigh, this is dependend on the clock / framerate changes - * made by the frequency control, sick. - * - * Note we cannot use v4l2_ctrl_g_ctrl here, as we get called - * from ov519.c:setfreq() with the ctrl lock held! - */ - if (sd->freq->val == 1) { + /* Sigh, this is dependend on the clock / framerate changes + made by the frequency control, sick. */ + if (sd->ctrls[FREQ].val == 1) { start_cropx = 277; start_cropy = 37; } else { @@ -479,9 +474,8 @@ static void w9968cf_mode_init_regs(struct sd *sd) /* We may get called multiple times (usb isoc bw negotiat.) */ jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height, sd->gspca_dev.width, 0x22); /* JPEG 420 */ - jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); + jpeg_set_qual(sd->jpeg_hdr, sd->quality); w9968cf_upload_quantizationtables(sd); - v4l2_ctrl_grab(sd->jpegqual, true); } /* Video Capture Control Register */ @@ -520,7 +514,6 @@ static void w9968cf_mode_init_regs(struct sd *sd) static void w9968cf_stop0(struct sd *sd) { - v4l2_ctrl_grab(sd->jpegqual, false); reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */ reg_w(sd, 0x16, 0x0000); /* stop video capture */ } diff --git a/trunk/drivers/media/video/gspca/xirlink_cit.c b/trunk/drivers/media/video/gspca/xirlink_cit.c index 13b8d395d210..ecada178bceb 100644 --- a/trunk/drivers/media/video/gspca/xirlink_cit.c +++ b/trunk/drivers/media/video/gspca/xirlink_cit.c @@ -53,7 +53,6 @@ MODULE_PARM_DESC(rca_input, /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct v4l2_ctrl *lighting; u8 model; #define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */ #define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */ @@ -66,10 +65,127 @@ struct sd { u8 stop_on_control_change; u8 sof_read; u8 sof_len; + u8 contrast; + u8 brightness; + u8 hue; + u8 sharpness; + u8 lighting; + u8 hflip; }; +/* 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_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setlighting(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); static void sd_stop0(struct gspca_dev *gspca_dev); +static const struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 63, + .step = 1, +#define BRIGHTNESS_DEFAULT 32 + .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 = 20, + .step = 1, +#define CONTRAST_DEFAULT 10 + .default_value = CONTRAST_DEFAULT, + .flags = 0, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_HUE 2 + { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 127, + .step = 1, +#define HUE_DEFAULT 63 + .default_value = HUE_DEFAULT, + .flags = 0, + }, + .set = sd_sethue, + .get = sd_gethue, + }, +#define SD_SHARPNESS 3 + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 6, + .step = 1, +#define SHARPNESS_DEFAULT 3 + .default_value = SHARPNESS_DEFAULT, + .flags = 0, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, +#define SD_LIGHTING 4 + { + { + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Lighting", + .minimum = 0, + .maximum = 2, + .step = 1, +#define LIGHTING_DEFAULT 1 + .default_value = LIGHTING_DEFAULT, + .flags = 0, + }, + .set = sd_setlighting, + .get = sd_getlighting, + }, +#define SD_HFLIP 5 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEFAULT 0 + .default_value = HFLIP_DEFAULT, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, +}; + static const struct v4l2_pix_format cif_yuv_mode[] = { {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE, .bytesperline = 176, @@ -879,36 +995,56 @@ static int sd_config(struct gspca_dev *gspca_dev, case CIT_MODEL0: cam->cam_mode = model0_mode; cam->nmodes = ARRAY_SIZE(model0_mode); + gspca_dev->ctrl_dis = ~((1 << SD_CONTRAST) | (1 << SD_HFLIP)); sd->sof_len = 4; break; case CIT_MODEL1: cam->cam_mode = cif_yuv_mode; cam->nmodes = ARRAY_SIZE(cif_yuv_mode); + gspca_dev->ctrl_dis = (1 << SD_HUE) | (1 << SD_HFLIP); sd->sof_len = 4; break; case CIT_MODEL2: cam->cam_mode = model2_mode + 1; /* no 160x120 */ cam->nmodes = 3; + gspca_dev->ctrl_dis = (1 << SD_CONTRAST) | + (1 << SD_SHARPNESS) | + (1 << SD_HFLIP); break; case CIT_MODEL3: cam->cam_mode = vga_yuv_mode; cam->nmodes = ARRAY_SIZE(vga_yuv_mode); + gspca_dev->ctrl_dis = (1 << SD_HUE) | + (1 << SD_LIGHTING) | + (1 << SD_HFLIP); sd->stop_on_control_change = 1; sd->sof_len = 4; break; case CIT_MODEL4: cam->cam_mode = model2_mode; cam->nmodes = ARRAY_SIZE(model2_mode); + gspca_dev->ctrl_dis = (1 << SD_CONTRAST) | + (1 << SD_SHARPNESS) | + (1 << SD_LIGHTING) | + (1 << SD_HFLIP); break; case CIT_IBM_NETCAM_PRO: cam->cam_mode = vga_yuv_mode; cam->nmodes = 2; /* no 640 x 480 */ cam->input_flags = V4L2_IN_ST_VFLIP; + gspca_dev->ctrl_dis = ~(1 << SD_CONTRAST); sd->stop_on_control_change = 1; sd->sof_len = 4; break; } + sd->brightness = BRIGHTNESS_DEFAULT; + sd->contrast = CONTRAST_DEFAULT; + sd->hue = HUE_DEFAULT; + sd->sharpness = SHARPNESS_DEFAULT; + sd->lighting = LIGHTING_DEFAULT; + sd->hflip = HFLIP_DEFAULT; + return 0; } @@ -1151,7 +1287,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val) +static int cit_set_brightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; @@ -1163,19 +1299,19 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val) break; case CIT_MODEL1: /* Model 1: Brightness range 0 - 63 */ - cit_Packet_Format1(gspca_dev, 0x0031, val); - cit_Packet_Format1(gspca_dev, 0x0032, val); - cit_Packet_Format1(gspca_dev, 0x0033, val); + cit_Packet_Format1(gspca_dev, 0x0031, sd->brightness); + cit_Packet_Format1(gspca_dev, 0x0032, sd->brightness); + cit_Packet_Format1(gspca_dev, 0x0033, sd->brightness); break; case CIT_MODEL2: /* Model 2: Brightness range 0x60 - 0xee */ /* Scale 0 - 63 to 0x60 - 0xee */ - i = 0x60 + val * 2254 / 1000; + i = 0x60 + sd->brightness * 2254 / 1000; cit_model2_Packet1(gspca_dev, 0x001a, i); break; case CIT_MODEL3: /* Model 3: Brightness range 'i' in [0x0C..0x3F] */ - i = val; + i = sd->brightness; if (i < 0x0c) i = 0x0c; cit_model3_Packet1(gspca_dev, 0x0036, i); @@ -1183,7 +1319,7 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val) case CIT_MODEL4: /* Model 4: Brightness range 'i' in [0x04..0xb4] */ /* Scale 0 - 63 to 0x04 - 0xb4 */ - i = 0x04 + val * 2794 / 1000; + i = 0x04 + sd->brightness * 2794 / 1000; cit_model4_BrightnessPacket(gspca_dev, i); break; } @@ -1191,7 +1327,7 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val) return 0; } -static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val) +static int cit_set_contrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1199,16 +1335,16 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val) case CIT_MODEL0: { int i; /* gain 0-15, 0-20 -> 0-15 */ - i = val * 1000 / 1333; + i = sd->contrast * 1000 / 1333; cit_write_reg(gspca_dev, i, 0x0422); /* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */ - i = val * 2000 / 1333; + i = sd->contrast * 2000 / 1333; cit_write_reg(gspca_dev, i, 0x0423); /* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63 */ - i = val * 4000 / 1333; + i = sd->contrast * 4000 / 1333; cit_write_reg(gspca_dev, i, 0x0424); /* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */ - i = val * 8000 / 1333; + i = sd->contrast * 8000 / 1333; cit_write_reg(gspca_dev, i, 0x0425); break; } @@ -1219,7 +1355,7 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val) case CIT_MODEL1: { /* Scale 0 - 20 to 15 - 0 */ - int i, new_contrast = (20 - val) * 1000 / 1333; + int i, new_contrast = (20 - sd->contrast) * 1000 / 1333; for (i = 0; i < cit_model1_ntries; i++) { cit_Packet_Format1(gspca_dev, 0x0014, new_contrast); cit_send_FF_04_02(gspca_dev); @@ -1241,20 +1377,20 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val) { 0x01, 0x0e, 0x16 }, { 0x01, 0x10, 0x16 } /* Maximum */ }; - int i = val / 3; + int i = sd->contrast / 3; cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1); cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2); cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3); break; } case CIT_IBM_NETCAM_PRO: - cit_model3_Packet1(gspca_dev, 0x005b, val + 1); + cit_model3_Packet1(gspca_dev, 0x005b, sd->contrast + 1); break; } return 0; } -static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val) +static int cit_set_hue(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1265,7 +1401,7 @@ static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val) /* No hue control for these models */ break; case CIT_MODEL2: - cit_model2_Packet1(gspca_dev, 0x0024, val); + cit_model2_Packet1(gspca_dev, 0x0024, sd->hue); /* cit_model2_Packet1(gspca_dev, 0x0020, sat); */ break; case CIT_MODEL3: { @@ -1273,7 +1409,7 @@ static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val) /* TESTME according to the ibmcam driver this does not work */ if (0) { /* Scale 0 - 127 to 0x05 - 0x37 */ - int i = 0x05 + val * 1000 / 2540; + int i = 0x05 + sd->hue * 1000 / 2540; cit_model3_Packet1(gspca_dev, 0x007e, i); } break; @@ -1299,14 +1435,14 @@ static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val) cit_write_reg(gspca_dev, 160, 0x012e); /* Red gain */ cit_write_reg(gspca_dev, 160, 0x0130); /* Blue gain */ cit_write_reg(gspca_dev, 0x8a28, 0x0124); - cit_write_reg(gspca_dev, val, 0x012d); /* Hue */ + cit_write_reg(gspca_dev, sd->hue, 0x012d); /* Hue */ cit_write_reg(gspca_dev, 0xf545, 0x0124); break; } return 0; } -static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val) +static int cit_set_sharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1323,7 +1459,7 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val) 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a }; for (i = 0; i < cit_model1_ntries; i++) - cit_PacketFormat2(gspca_dev, 0x0013, sa[val]); + cit_PacketFormat2(gspca_dev, 0x0013, sa[sd->sharpness]); break; } case CIT_MODEL3: @@ -1346,10 +1482,10 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val) { 0x03, 0x06, 0x05, 0x14 }, { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */ }; - cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1); - cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2); - cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3); - cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4); + cit_model3_Packet1(gspca_dev, 0x0060, sv[sd->sharpness].sv1); + cit_model3_Packet1(gspca_dev, 0x0061, sv[sd->sharpness].sv2); + cit_model3_Packet1(gspca_dev, 0x0062, sv[sd->sharpness].sv3); + cit_model3_Packet1(gspca_dev, 0x0063, sv[sd->sharpness].sv4); break; } } @@ -1374,7 +1510,7 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val) * 1/5/00 Created. * 2/20/00 Added support for Model 2 cameras. */ -static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val) +static void cit_set_lighting(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1388,19 +1524,19 @@ static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val) case CIT_MODEL1: { int i; for (i = 0; i < cit_model1_ntries; i++) - cit_Packet_Format1(gspca_dev, 0x0027, val); + cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting); break; } } } -static void cit_set_hflip(struct gspca_dev *gspca_dev, s32 val) +static void cit_set_hflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; switch (sd->model) { case CIT_MODEL0: - if (val) + if (sd->hflip) cit_write_reg(gspca_dev, 0x0020, 0x0115); else cit_write_reg(gspca_dev, 0x0040, 0x0115); @@ -1695,8 +1831,7 @@ static int cit_start_model1(struct gspca_dev *gspca_dev) cit_PacketFormat2(gspca_dev, 0x13, 0x1a); /* Default lighting conditions */ - cit_Packet_Format1(gspca_dev, 0x0027, - v4l2_ctrl_g_ctrl(sd->lighting)); + cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting); } /* Assorted init */ @@ -1914,10 +2049,9 @@ static int cit_start_model2(struct gspca_dev *gspca_dev) break; } - cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting)); - /* model2 cannot change the backlight compensation while streaming */ - v4l2_ctrl_grab(sd->lighting, true); - + /* FIXME this cannot be changed while streaming, so we + should report a grabbed flag for this control. */ + cit_model2_Packet1(gspca_dev, 0x0028, sd->lighting); /* color balance rg2 */ cit_model2_Packet1(gspca_dev, 0x001e, 0x002f); /* saturation */ @@ -2621,6 +2755,13 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } + cit_set_brightness(gspca_dev); + cit_set_contrast(gspca_dev); + cit_set_hue(gspca_dev); + cit_set_sharpness(gspca_dev); + cit_set_lighting(gspca_dev); + cit_set_hflip(gspca_dev); + /* Program max isoc packet size */ cit_write_reg(gspca_dev, packet_size >> 8, 0x0106); cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107); @@ -2716,8 +2857,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */ break; case CIT_MODEL2: - v4l2_ctrl_grab(sd->lighting, false); - /* Fall through! */ case CIT_MODEL4: cit_model2_Packet1(gspca_dev, 0x0030, 0x0004); @@ -2916,6 +3055,152 @@ 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) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) { + if (sd->stop_on_control_change) + sd_stopN(gspca_dev); + cit_set_brightness(gspca_dev); + if (sd->stop_on_control_change) + cit_restart_stream(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) { + if (sd->stop_on_control_change) + sd_stopN(gspca_dev); + cit_set_contrast(gspca_dev); + if (sd->stop_on_control_change) + cit_restart_stream(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_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) { + if (sd->stop_on_control_change) + sd_stopN(gspca_dev); + cit_set_hue(gspca_dev); + if (sd->stop_on_control_change) + cit_restart_stream(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; + + 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) { + if (sd->stop_on_control_change) + sd_stopN(gspca_dev); + cit_set_sharpness(gspca_dev); + if (sd->stop_on_control_change) + cit_restart_stream(gspca_dev); + } + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + + return 0; +} + +static int sd_setlighting(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lighting = val; + if (gspca_dev->streaming) { + if (sd->stop_on_control_change) + sd_stopN(gspca_dev); + cit_set_lighting(gspca_dev); + if (sd->stop_on_control_change) + cit_restart_stream(gspca_dev); + } + return 0; +} + +static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lighting; + + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) { + if (sd->stop_on_control_change) + sd_stopN(gspca_dev); + cit_set_hflip(gspca_dev); + if (sd->stop_on_control_change) + cit_restart_stream(gspca_dev); + } + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + + return 0; +} + #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static void cit_check_button(struct gspca_dev *gspca_dev) { @@ -2949,117 +3234,13 @@ static void cit_check_button(struct gspca_dev *gspca_dev) } #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; - - if (sd->stop_on_control_change) - sd_stopN(gspca_dev); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - cit_set_brightness(gspca_dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - cit_set_contrast(gspca_dev, ctrl->val); - break; - case V4L2_CID_HUE: - cit_set_hue(gspca_dev, ctrl->val); - break; - case V4L2_CID_HFLIP: - cit_set_hflip(gspca_dev, ctrl->val); - break; - case V4L2_CID_SHARPNESS: - cit_set_sharpness(gspca_dev, ctrl->val); - break; - case V4L2_CID_BACKLIGHT_COMPENSATION: - cit_set_lighting(gspca_dev, ctrl->val); - break; - } - if (sd->stop_on_control_change) - cit_restart_stream(gspca_dev); - 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; - bool has_brightness; - bool has_contrast; - bool has_hue; - bool has_sharpness; - bool has_lighting; - bool has_hflip; - - has_brightness = has_contrast = has_hue = - has_sharpness = has_hflip = has_lighting = false; - switch (sd->model) { - case CIT_MODEL0: - has_contrast = has_hflip = true; - break; - case CIT_MODEL1: - has_brightness = has_contrast = - has_sharpness = has_lighting = true; - break; - case CIT_MODEL2: - has_brightness = has_hue = has_lighting = true; - break; - case CIT_MODEL3: - has_brightness = has_contrast = has_sharpness = true; - break; - case CIT_MODEL4: - has_brightness = has_hue = true; - break; - case CIT_IBM_NETCAM_PRO: - has_brightness = has_hue = - has_sharpness = has_hflip = has_lighting = true; - break; - } - gspca_dev->vdev.ctrl_handler = hdl; - v4l2_ctrl_handler_init(hdl, 5); - if (has_brightness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 63, 1, 32); - if (has_contrast) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_CONTRAST, 0, 20, 1, 10); - if (has_hue) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HUE, 0, 127, 1, 63); - if (has_sharpness) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 6, 1, 3); - if (has_lighting) - sd->lighting = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_BACKLIGHT_COMPENSATION, 0, 2, 1, 1); - if (has_hflip) - v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - - 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, @@ -3072,9 +3253,10 @@ static const struct sd_desc sd_desc = { static const struct sd_desc sd_desc_isoc_nego = { .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, .isoc_init = sd_isoc_init, .isoc_nego = sd_isoc_nego, @@ -3138,7 +3320,6 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, - .reset_resume = gspca_resume, #endif }; diff --git a/trunk/drivers/media/video/m5mols/m5mols_controls.c b/trunk/drivers/media/video/m5mols/m5mols_controls.c index fdbc205a2969..392a028730e2 100644 --- a/trunk/drivers/media/video/m5mols/m5mols_controls.c +++ b/trunk/drivers/media/video/m5mols/m5mols_controls.c @@ -527,8 +527,8 @@ static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { /* Supported manual ISO values */ static const s64 iso_qmenu[] = { - /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */ - 50000, 100000, 200000, 400000, 800000, 1600000, 3200000 + /* AE_ISO: 0x01...0x07 */ + 50, 100, 200, 400, 800, 1600, 3200 }; /* Supported Exposure Bias values, -2.0EV...+2.0EV */ diff --git a/trunk/drivers/media/video/mem2mem_testdev.c b/trunk/drivers/media/video/mem2mem_testdev.c index 7efe9ad7acc7..f08cf38a496d 100644 --- a/trunk/drivers/media/video/mem2mem_testdev.c +++ b/trunk/drivers/media/video/mem2mem_testdev.c @@ -27,8 +27,6 @@ #include #include #include -#include -#include #include #define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev" @@ -103,8 +101,6 @@ static struct m2mtest_fmt formats[] = { }, }; -#define NUM_FORMATS ARRAY_SIZE(formats) - /* Per-queue, driver-specific private data */ struct m2mtest_q_data { unsigned int width; @@ -118,8 +114,50 @@ enum { V4L2_M2M_DST = 1, }; -#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000) -#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001) +#define V4L2_CID_TRANS_TIME_MSEC V4L2_CID_PRIVATE_BASE +#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_PRIVATE_BASE + 1) + +static struct v4l2_queryctrl m2mtest_ctrls[] = { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_TRANS_TIME_MSEC, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Transaction time (msec)", + .minimum = 1, + .maximum = 10000, + .step = 100, + .default_value = 1000, + .flags = 0, + }, { + .id = V4L2_CID_TRANS_NUM_BUFS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Buffers per transaction", + .minimum = 1, + .maximum = MEM2MEM_DEF_NUM_BUFS, + .step = 1, + .default_value = 1, + .flags = 0, + }, +}; + +#define NUM_FORMATS ARRAY_SIZE(formats) static struct m2mtest_fmt *find_format(struct v4l2_format *f) { @@ -152,11 +190,8 @@ struct m2mtest_dev { }; struct m2mtest_ctx { - struct v4l2_fh fh; struct m2mtest_dev *dev; - struct v4l2_ctrl_handler hdl; - /* Processed buffers in this transaction */ u8 num_processed; @@ -171,19 +206,12 @@ struct m2mtest_ctx { /* Processing mode */ int mode; - enum v4l2_colorspace colorspace; - struct v4l2_m2m_ctx *m2m_ctx; /* Source and destination queue data */ struct m2mtest_q_data q_data[2]; }; -static inline struct m2mtest_ctx *file2ctx(struct file *file) -{ - return container_of(file->private_data, struct m2mtest_ctx, fh); -} - static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx, enum v4l2_buf_type type) { @@ -199,6 +227,18 @@ static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx, } +static struct v4l2_queryctrl *get_ctrl(int id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(m2mtest_ctrls); ++i) { + if (id == m2mtest_ctrls[i].id) + return &m2mtest_ctrls[i]; + } + + return NULL; +} + static int device_process(struct m2mtest_ctx *ctx, struct vb2_buffer *in_vb, struct vb2_buffer *out_vb) @@ -430,9 +470,10 @@ static int vidioc_querycap(struct file *file, void *priv, { strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); - strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + cap->bus_info[0] = 0; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING; + return 0; } @@ -495,7 +536,6 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) f->fmt.pix.pixelformat = q_data->fmt->fourcc; f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; f->fmt.pix.sizeimage = q_data->sizeimage; - f->fmt.pix.colorspace = ctx->colorspace; return 0; } @@ -503,13 +543,13 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) static int vidioc_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { - return vidioc_g_fmt(file2ctx(file), f); + return vidioc_g_fmt(priv, f); } static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - return vidioc_g_fmt(file2ctx(file), f); + return vidioc_g_fmt(priv, f); } static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt) @@ -548,7 +588,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct m2mtest_fmt *fmt; - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = priv; fmt = find_format(f); if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { @@ -557,7 +597,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat); return -EINVAL; } - f->fmt.pix.colorspace = ctx->colorspace; return vidioc_try_fmt(f, fmt); } @@ -566,7 +605,7 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct m2mtest_fmt *fmt; - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = priv; fmt = find_format(f); if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { @@ -575,8 +614,6 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, f->fmt.pix.pixelformat); return -EINVAL; } - if (!f->fmt.pix.colorspace) - f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; return vidioc_try_fmt(f, fmt); } @@ -621,29 +658,25 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, if (ret) return ret; - return vidioc_s_fmt(file2ctx(file), f); + return vidioc_s_fmt(priv, f); } static int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { - struct m2mtest_ctx *ctx = file2ctx(file); int ret; ret = vidioc_try_fmt_vid_out(file, priv, f); if (ret) return ret; - ret = vidioc_s_fmt(file2ctx(file), f); - if (!ret) - ctx->colorspace = f->fmt.pix.colorspace; - return ret; + return vidioc_s_fmt(priv, f); } static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = priv; return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } @@ -651,21 +684,21 @@ static int vidioc_reqbufs(struct file *file, void *priv, static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = priv; return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = priv; return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = priv; return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } @@ -673,7 +706,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = priv; return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); } @@ -681,37 +714,101 @@ static int vidioc_streamon(struct file *file, void *priv, static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = priv; return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); } -static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl) +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct v4l2_queryctrl *c; + + c = get_ctrl(qc->id); + if (!c) + return -EINVAL; + + *qc = *c; + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { - struct m2mtest_ctx *ctx = - container_of(ctrl->handler, struct m2mtest_ctx, hdl); + struct m2mtest_ctx *ctx = priv; switch (ctrl->id) { case V4L2_CID_HFLIP: - if (ctrl->val) + ctrl->value = (ctx->mode & MEM2MEM_HFLIP) ? 1 : 0; + break; + + case V4L2_CID_VFLIP: + ctrl->value = (ctx->mode & MEM2MEM_VFLIP) ? 1 : 0; + break; + + case V4L2_CID_TRANS_TIME_MSEC: + ctrl->value = ctx->transtime; + break; + + case V4L2_CID_TRANS_NUM_BUFS: + ctrl->value = ctx->translen; + break; + + default: + v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n"); + return -EINVAL; + } + + return 0; +} + +static int check_ctrl_val(struct m2mtest_ctx *ctx, struct v4l2_control *ctrl) +{ + struct v4l2_queryctrl *c; + + c = get_ctrl(ctrl->id); + if (!c) + return -EINVAL; + + if (ctrl->value < c->minimum || ctrl->value > c->maximum) { + v4l2_err(&ctx->dev->v4l2_dev, "Value out of range\n"); + return -ERANGE; + } + + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct m2mtest_ctx *ctx = priv; + int ret = 0; + + ret = check_ctrl_val(ctx, ctrl); + if (ret != 0) + return ret; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + if (ctrl->value) ctx->mode |= MEM2MEM_HFLIP; else ctx->mode &= ~MEM2MEM_HFLIP; break; case V4L2_CID_VFLIP: - if (ctrl->val) + if (ctrl->value) ctx->mode |= MEM2MEM_VFLIP; else ctx->mode &= ~MEM2MEM_VFLIP; break; case V4L2_CID_TRANS_TIME_MSEC: - ctx->transtime = ctrl->val; + ctx->transtime = ctrl->value; break; case V4L2_CID_TRANS_NUM_BUFS: - ctx->translen = ctrl->val; + ctx->translen = ctrl->value; break; default: @@ -722,10 +819,6 @@ static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } -static const struct v4l2_ctrl_ops m2mtest_ctrl_ops = { - .s_ctrl = m2mtest_s_ctrl, -}; - static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { .vidioc_querycap = vidioc_querycap, @@ -748,8 +841,10 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; @@ -861,28 +956,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds return vb2_queue_init(dst_vq); } -static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = { - .ops = &m2mtest_ctrl_ops, - .id = V4L2_CID_TRANS_TIME_MSEC, - .name = "Transaction Time (msec)", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 1001, - .min = 1, - .max = 10001, - .step = 100, -}; - -static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = { - .ops = &m2mtest_ctrl_ops, - .id = V4L2_CID_TRANS_NUM_BUFS, - .name = "Buffers Per Transaction", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 1, - .min = 1, - .max = MEM2MEM_DEF_NUM_BUFS, - .step = 1, -}; - /* * File operations */ @@ -890,51 +963,30 @@ static int m2mtest_open(struct file *file) { struct m2mtest_dev *dev = video_drvdata(file); struct m2mtest_ctx *ctx = NULL; - struct v4l2_ctrl_handler *hdl; ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) return -ENOMEM; - v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; + file->private_data = ctx; ctx->dev = dev; - hdl = &ctx->hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_time_msec, NULL); - v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_num_bufs, NULL); - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - return err; - } - ctx->fh.ctrl_handler = hdl; - v4l2_ctrl_handler_setup(hdl); + ctx->translen = MEM2MEM_DEF_TRANSLEN; + ctx->transtime = MEM2MEM_DEF_TRANSTIME; + ctx->num_processed = 0; + ctx->mode = 0; ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0]; - ctx->q_data[V4L2_M2M_SRC].width = 640; - ctx->q_data[V4L2_M2M_SRC].height = 480; - ctx->q_data[V4L2_M2M_SRC].sizeimage = - ctx->q_data[V4L2_M2M_SRC].width * - ctx->q_data[V4L2_M2M_SRC].height * - (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3); - ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; - ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->q_data[V4L2_M2M_DST].fmt = &formats[0]; ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); if (IS_ERR(ctx->m2m_ctx)) { int ret = PTR_ERR(ctx->m2m_ctx); - v4l2_ctrl_handler_free(hdl); kfree(ctx); return ret; } - v4l2_fh_add(&ctx->fh); atomic_inc(&dev->num_inst); dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); @@ -945,13 +997,10 @@ static int m2mtest_open(struct file *file) static int m2mtest_release(struct file *file) { struct m2mtest_dev *dev = video_drvdata(file); - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = file->private_data; dprintk(dev, "Releasing instance %p\n", ctx); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - v4l2_ctrl_handler_free(&ctx->hdl); v4l2_m2m_ctx_release(ctx->m2m_ctx); kfree(ctx); @@ -963,14 +1012,14 @@ static int m2mtest_release(struct file *file) static unsigned int m2mtest_poll(struct file *file, struct poll_table_struct *wait) { - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = file->private_data; return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); } static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma) { - struct m2mtest_ctx *ctx = file2ctx(file); + struct m2mtest_ctx *ctx = file->private_data; return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); } diff --git a/trunk/drivers/media/video/mx2_emmaprp.c b/trunk/drivers/media/video/mx2_emmaprp.c index 5f8a6f5b98f9..0bd5815de369 100644 --- a/trunk/drivers/media/video/mx2_emmaprp.c +++ b/trunk/drivers/media/video/mx2_emmaprp.c @@ -396,13 +396,9 @@ static int vidioc_querycap(struct file *file, void *priv, { strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | - V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING; + return 0; } diff --git a/trunk/drivers/media/video/ov2640.c b/trunk/drivers/media/video/ov2640.c index 7c44d1fe3c87..3c2c5d3bcc6b 100644 --- a/trunk/drivers/media/video/ov2640.c +++ b/trunk/drivers/media/video/ov2640.c @@ -837,8 +837,10 @@ static int ov2640_g_fmt(struct v4l2_subdev *sd, if (!priv->win) { u32 width = W_SVGA, height = H_SVGA; - priv->win = ov2640_select_win(&width, &height); - priv->cfmt_code = V4L2_MBUS_FMT_UYVY8_2X8; + int ret = ov2640_set_params(client, &width, &height, + V4L2_MBUS_FMT_UYVY8_2X8); + if (ret < 0) + return ret; } mf->width = priv->win->width; diff --git a/trunk/drivers/media/video/ov772x.c b/trunk/drivers/media/video/ov772x.c index 6d79b89b8603..74e77d327ed8 100644 --- a/trunk/drivers/media/video/ov772x.c +++ b/trunk/drivers/media/video/ov772x.c @@ -880,11 +880,15 @@ static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) static int ov772x_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev); if (!priv->win || !priv->cfmt) { - priv->cfmt = &ov772x_cfmts[0]; - priv->win = ov772x_select_win(VGA_WIDTH, VGA_HEIGHT); + u32 width = VGA_WIDTH, height = VGA_HEIGHT; + int ret = ov772x_set_params(client, &width, &height, + V4L2_MBUS_FMT_YUYV8_2X8); + if (ret < 0) + return ret; } mf->width = priv->win->width; diff --git a/trunk/drivers/media/video/ov9640.c b/trunk/drivers/media/video/ov9640.c index 9ed4ba4236c4..23412debb36b 100644 --- a/trunk/drivers/media/video/ov9640.c +++ b/trunk/drivers/media/video/ov9640.c @@ -605,7 +605,6 @@ static int ov9640_video_probe(struct i2c_client *client) devname = "ov9640"; priv->model = V4L2_IDENT_OV9640; priv->revision = 2; - break; case OV9640_V3: devname = "ov9640"; priv->model = V4L2_IDENT_OV9640; diff --git a/trunk/drivers/media/video/pms.c b/trunk/drivers/media/video/pms.c index 77f9c92186f4..b4c679b3fb0f 100644 --- a/trunk/drivers/media/video/pms.c +++ b/trunk/drivers/media/video/pms.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/trunk/drivers/media/video/s2255drv.c b/trunk/drivers/media/video/s2255drv.c index 95007dda0c93..01c2179f0520 100644 --- a/trunk/drivers/media/video/s2255drv.c +++ b/trunk/drivers/media/video/s2255drv.c @@ -2686,4 +2686,3 @@ MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver"); MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)"); MODULE_LICENSE("GPL"); MODULE_VERSION(S2255_VERSION); -MODULE_FIRMWARE(FIRMWARE_FILE_NAME); diff --git a/trunk/drivers/media/video/s5p-fimc/fimc-capture.c b/trunk/drivers/media/video/s5p-fimc/fimc-capture.c index 8e413dd3c0b0..6a34183564d2 100644 --- a/trunk/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/trunk/drivers/media/video/s5p-fimc/fimc-capture.c @@ -480,59 +480,48 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc); static int fimc_capture_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); - int ret = -EBUSY; + int ret; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - if (fimc_m2m_active(fimc)) - goto unlock; + return -EBUSY; set_bit(ST_CAPT_BUSY, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); if (ret < 0) - goto unlock; + return ret; ret = v4l2_fh_open(file); - if (ret) { - pm_runtime_put(&fimc->pdev->dev); - goto unlock; - } + if (ret) + return ret; - if (++fimc->vid_cap.refcnt == 1) { - ret = fimc_pipeline_initialize(&fimc->pipeline, - &fimc->vid_cap.vfd->entity, true); + if (++fimc->vid_cap.refcnt != 1) + return 0; - if (!ret && !fimc->vid_cap.user_subdev_api) - ret = fimc_capture_set_default_format(fimc); + ret = fimc_pipeline_initialize(&fimc->pipeline, + &fimc->vid_cap.vfd->entity, true); + if (ret < 0) { + clear_bit(ST_CAPT_BUSY, &fimc->state); + pm_runtime_put_sync(&fimc->pdev->dev); + fimc->vid_cap.refcnt--; + v4l2_fh_release(file); + return ret; + } + ret = fimc_capture_ctrls_create(fimc); - if (!ret) - ret = fimc_capture_ctrls_create(fimc); + if (!ret && !fimc->vid_cap.user_subdev_api) + ret = fimc_capture_set_default_format(fimc); - if (ret < 0) { - clear_bit(ST_CAPT_BUSY, &fimc->state); - pm_runtime_put_sync(&fimc->pdev->dev); - fimc->vid_cap.refcnt--; - v4l2_fh_release(file); - } - } -unlock: - mutex_unlock(&fimc->lock); return ret; } static int fimc_capture_close(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); - int ret; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - if (--fimc->vid_cap.refcnt == 0) { clear_bit(ST_CAPT_BUSY, &fimc->state); fimc_stop_capture(fimc, false); @@ -546,40 +535,22 @@ static int fimc_capture_close(struct file *file) vb2_queue_release(&fimc->vid_cap.vbq); fimc_ctrls_delete(fimc->vid_cap.ctx); } - - ret = v4l2_fh_release(file); - - mutex_unlock(&fimc->lock); - return ret; + return v4l2_fh_release(file); } static unsigned int fimc_capture_poll(struct file *file, struct poll_table_struct *wait) { struct fimc_dev *fimc = video_drvdata(file); - int ret; - if (mutex_lock_interruptible(&fimc->lock)) - return POLL_ERR; - - ret = vb2_poll(&fimc->vid_cap.vbq, file, wait); - mutex_unlock(&fimc->lock); - - return ret; + return vb2_poll(&fimc->vid_cap.vbq, file, wait); } static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) { struct fimc_dev *fimc = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - ret = vb2_mmap(&fimc->vid_cap.vbq, vma); - mutex_unlock(&fimc->lock); - - return ret; + return vb2_mmap(&fimc->vid_cap.vbq, vma); } static const struct v4l2_file_operations fimc_capture_fops = { @@ -1618,7 +1589,10 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; - + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); video_set_drvdata(vfd, fimc); vid_cap = &fimc->vid_cap; diff --git a/trunk/drivers/media/video/s5p-fimc/fimc-core.h b/trunk/drivers/media/video/s5p-fimc/fimc-core.h index 808ccc621846..95b27ae5cf27 100644 --- a/trunk/drivers/media/video/s5p-fimc/fimc-core.h +++ b/trunk/drivers/media/video/s5p-fimc/fimc-core.h @@ -27,6 +27,9 @@ #include #include +#define err(fmt, args...) \ + printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args) + #define dbg(fmt, args...) \ pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args) diff --git a/trunk/drivers/media/video/s5p-fimc/fimc-m2m.c b/trunk/drivers/media/video/s5p-fimc/fimc-m2m.c index c587011d80ef..4c58e0570962 100644 --- a/trunk/drivers/media/video/s5p-fimc/fimc-m2m.c +++ b/trunk/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -259,12 +259,7 @@ static int fimc_m2m_querycap(struct file *file, void *fh, strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; @@ -647,25 +642,21 @@ static int fimc_m2m_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); struct fimc_ctx *ctx; - int ret = -EBUSY; + int ret; dbg("pid: %d, state: 0x%lx, refcnt: %d", task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; /* * Return if the corresponding video capture node * is already opened. */ if (fimc->vid_cap.refcnt > 0) - goto unlock; + return -EBUSY; ctx = kzalloc(sizeof *ctx, GFP_KERNEL); - if (!ctx) { - ret = -ENOMEM; - goto unlock; - } + if (!ctx) + return -ENOMEM; v4l2_fh_init(&ctx->fh, fimc->m2m.vfd); ctx->fimc_dev = fimc; @@ -696,8 +687,6 @@ static int fimc_m2m_open(struct file *file) if (fimc->m2m.refcnt++ == 0) set_bit(ST_M2M_RUN, &fimc->state); - - mutex_unlock(&fimc->lock); return 0; error_c: @@ -706,8 +695,6 @@ static int fimc_m2m_open(struct file *file) v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); kfree(ctx); -unlock: - mutex_unlock(&fimc->lock); return ret; } @@ -719,9 +706,6 @@ static int fimc_m2m_release(struct file *file) dbg("pid: %d, state: 0x%lx, refcnt= %d", task_pid_nr(current), fimc->state, fimc->m2m.refcnt); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - v4l2_m2m_ctx_release(ctx->m2m_ctx); fimc_ctrls_delete(ctx); v4l2_fh_del(&ctx->fh); @@ -730,8 +714,6 @@ static int fimc_m2m_release(struct file *file) if (--fimc->m2m.refcnt <= 0) clear_bit(ST_M2M_RUN, &fimc->state); kfree(ctx); - - mutex_unlock(&fimc->lock); return 0; } @@ -739,32 +721,16 @@ static unsigned int fimc_m2m_poll(struct file *file, struct poll_table_struct *wait) { struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - struct fimc_dev *fimc = ctx->fimc_dev; - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - - ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); - mutex_unlock(&fimc->lock); - return ret; + return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); } static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) { struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - struct fimc_dev *fimc = ctx->fimc_dev; - int ret; - - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; - ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); - mutex_unlock(&fimc->lock); - - return ret; + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); } static const struct v4l2_file_operations fimc_m2m_fops = { @@ -806,6 +772,10 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); video_set_drvdata(vfd, fimc); diff --git a/trunk/drivers/media/video/s5p-fimc/fimc-reg.c b/trunk/drivers/media/video/s5p-fimc/fimc-reg.c index 0e3eb9ce4f98..1fc4ce8446f5 100644 --- a/trunk/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/trunk/drivers/media/video/s5p-fimc/fimc-reg.c @@ -667,8 +667,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB | FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG); - switch (cam->bus_type) { - case FIMC_MIPI_CSI2: + if (cam->bus_type == FIMC_MIPI_CSI2) { cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI; if (cam->mux_id == 0) @@ -684,24 +683,23 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, cfg |= FIMC_REG_CIGCTRL_CAM_JPEG; break; default: - v4l2_err(vid_cap->vfd, - "Not supported camera pixel format: %#x\n", + v4l2_err(fimc->vid_cap.vfd, + "Not supported camera pixel format: %d", vid_cap->mf.code); return -EINVAL; } tmp |= (csis_data_alignment == 32) << 8; writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT); - break; - case FIMC_ITU_601...FIMC_ITU_656: + + } else if (cam->bus_type == FIMC_ITU_601 || + cam->bus_type == FIMC_ITU_656) { if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A; - break; - case FIMC_LCD_WB: + } else if (cam->bus_type == FIMC_LCD_WB) { cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; - break; - default: - v4l2_err(vid_cap->vfd, "Invalid camera bus type selected\n"); + } else { + err("invalid camera bus type selected\n"); return -EINVAL; } writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); diff --git a/trunk/drivers/media/video/s5p-g2d/g2d.c b/trunk/drivers/media/video/s5p-g2d/g2d.c index 7c2200435206..7c98ee7377ee 100644 --- a/trunk/drivers/media/video/s5p-g2d/g2d.c +++ b/trunk/drivers/media/video/s5p-g2d/g2d.c @@ -290,13 +290,8 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | - V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING; return 0; } diff --git a/trunk/drivers/media/video/s5p-jpeg/jpeg-core.c b/trunk/drivers/media/video/s5p-jpeg/jpeg-core.c index 813b801238d1..95f23024b17d 100644 --- a/trunk/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/trunk/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -489,13 +489,9 @@ static int s5p_jpeg_querycap(struct file *file, void *priv, sizeof(cap->card)); } cap->bus_info[0] = 0; - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; + cap->capabilities = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OUTPUT; return 0; } diff --git a/trunk/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/trunk/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index c5d567f87d77..feea867f318c 100644 --- a/trunk/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/trunk/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -220,14 +220,8 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE | - V4L2_CAP_VIDEO_OUTPUT_MPLANE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; return 0; } diff --git a/trunk/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/trunk/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index aa1c244cf66e..158b78989b89 100644 --- a/trunk/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/trunk/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -779,14 +779,9 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE | - V4L2_CAP_VIDEO_OUTPUT_MPLANE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_VIDEO_OUTPUT_MPLANE + | V4L2_CAP_STREAMING; return 0; } diff --git a/trunk/drivers/media/video/saa7164/saa7164-i2c.c b/trunk/drivers/media/video/saa7164/saa7164-i2c.c index 4f7e3b42263f..26148f76cba2 100644 --- a/trunk/drivers/media/video/saa7164/saa7164-i2c.c +++ b/trunk/drivers/media/video/saa7164/saa7164-i2c.c @@ -69,6 +69,15 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) return retval; } +void saa7164_call_i2c_clients(struct saa7164_i2c *bus, unsigned int cmd, + void *arg) +{ + if (bus->i2c_rc != 0) + return; + + i2c_clients_command(&bus->i2c_adap, cmd, arg); +} + static u32 saa7164_functionality(struct i2c_adapter *adap) { return I2C_FUNC_I2C; @@ -97,14 +106,21 @@ int saa7164_i2c_register(struct saa7164_i2c *bus) dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr); - bus->i2c_adap = saa7164_i2c_adap_template; - bus->i2c_client = saa7164_i2c_client_template; + memcpy(&bus->i2c_adap, &saa7164_i2c_adap_template, + sizeof(bus->i2c_adap)); + + memcpy(&bus->i2c_algo, &saa7164_i2c_algo_template, + sizeof(bus->i2c_algo)); + + memcpy(&bus->i2c_client, &saa7164_i2c_client_template, + sizeof(bus->i2c_client)); 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, bus); i2c_add_adapter(&bus->i2c_adap); diff --git a/trunk/drivers/media/video/saa7164/saa7164.h b/trunk/drivers/media/video/saa7164/saa7164.h index 35219b9b0fbc..8d120e3baf70 100644 --- a/trunk/drivers/media/video/saa7164/saa7164.h +++ b/trunk/drivers/media/video/saa7164/saa7164.h @@ -46,6 +46,7 @@ #include #include +#include #include #include #include @@ -250,6 +251,7 @@ struct saa7164_i2c { /* I2C I/O */ struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; struct i2c_client i2c_client; u32 i2c_rc; }; diff --git a/trunk/drivers/media/video/smiapp/smiapp-core.c b/trunk/drivers/media/video/smiapp/smiapp-core.c index bfd47c106134..f466a7edcb2a 100644 --- a/trunk/drivers/media/video/smiapp/smiapp-core.c +++ b/trunk/drivers/media/video/smiapp/smiapp-core.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/trunk/drivers/media/video/soc_camera.c b/trunk/drivers/media/video/soc_camera.c index b03ffecb7438..0421bf9453b4 100644 --- a/trunk/drivers/media/video/soc_camera.c +++ b/trunk/drivers/media/video/soc_camera.c @@ -62,7 +62,7 @@ static int soc_camera_power_on(struct soc_camera_device *icd, } if (icl->power) { - ret = icl->power(icd->control, 1); + ret = icl->power(icd->pdev, 1); if (ret < 0) { dev_err(icd->pdev, "Platform failed to power-on the camera.\n"); @@ -78,7 +78,7 @@ static int soc_camera_power_on(struct soc_camera_device *icd, esdpwr: if (icl->power) - icl->power(icd->control, 0); + icl->power(icd->pdev, 0); elinkpwr: regulator_bulk_disable(icl->num_regulators, icl->regulators); @@ -95,7 +95,7 @@ static int soc_camera_power_off(struct soc_camera_device *icd, return ret; if (icl->power) { - ret = icl->power(icd->control, 0); + ret = icl->power(icd->pdev, 0); if (ret < 0) { dev_err(icd->pdev, "Platform failed to power-off the camera.\n"); @@ -1518,7 +1518,6 @@ static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev) } static struct platform_driver __refdata soc_camera_pdrv = { - .probe = soc_camera_pdrv_probe, .remove = __devexit_p(soc_camera_pdrv_remove), .driver = { .name = "soc-camera-pdrv", @@ -1528,7 +1527,7 @@ static struct platform_driver __refdata soc_camera_pdrv = { static int __init soc_camera_init(void) { - return platform_driver_register(&soc_camera_pdrv); + return platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe); } static void __exit soc_camera_exit(void) diff --git a/trunk/drivers/media/video/tlg2300/pd-main.c b/trunk/drivers/media/video/tlg2300/pd-main.c index 7b1f6ebd0e2c..c096b3f74200 100644 --- a/trunk/drivers/media/video/tlg2300/pd-main.c +++ b/trunk/drivers/media/video/tlg2300/pd-main.c @@ -53,8 +53,7 @@ int debug_mode; module_param(debug_mode, int, 0644); MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose"); -#define TLG2300_FIRMWARE "tlg2300_firmware.bin" -static const char *firmware_name = TLG2300_FIRMWARE; +static const char *firmware_name = "tlg2300_firmware.bin"; static struct usb_driver poseidon_driver; static LIST_HEAD(pd_device_list); @@ -533,4 +532,3 @@ MODULE_AUTHOR("Telegent Systems"); MODULE_DESCRIPTION("For tlg2300-based USB device "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.0.2"); -MODULE_FIRMWARE(TLG2300_FIRMWARE); diff --git a/trunk/drivers/media/video/tvp5150.c b/trunk/drivers/media/video/tvp5150.c index a751b6c146fd..0d897cb1774a 100644 --- a/trunk/drivers/media/video/tvp5150.c +++ b/trunk/drivers/media/video/tvp5150.c @@ -257,7 +257,7 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd) int opmode = 0; struct tvp5150 *decoder = to_tvp5150(sd); int input = 0; - int val; + unsigned char val; if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable) input = 8; diff --git a/trunk/drivers/media/video/tw9910.c b/trunk/drivers/media/video/tw9910.c index 9f53eacb66e3..8768efb8508a 100644 --- a/trunk/drivers/media/video/tw9910.c +++ b/trunk/drivers/media/video/tw9910.c @@ -699,9 +699,11 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd, struct tw9910_priv *priv = to_tw9910(client); if (!priv->scale) { - priv->scale = tw9910_select_norm(priv->norm, 640, 480); - if (!priv->scale) - return -EINVAL; + int ret; + u32 width = 640, height = 480; + ret = tw9910_set_frame(sd, &width, &height); + if (ret < 0) + return ret; } mf->width = priv->scale->width; diff --git a/trunk/drivers/media/video/v4l2-compat-ioctl32.c b/trunk/drivers/media/video/v4l2-compat-ioctl32.c index 9ebd5c540d10..ac365cfb3706 100644 --- a/trunk/drivers/media/video/v4l2-compat-ioctl32.c +++ b/trunk/drivers/media/video/v4l2-compat-ioctl32.c @@ -1025,7 +1025,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_ENUM_DV_TIMINGS: case VIDIOC_QUERY_DV_TIMINGS: case VIDIOC_DV_TIMINGS_CAP: - case VIDIOC_ENUM_FREQ_BANDS: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/trunk/drivers/media/video/v4l2-ctrls.c b/trunk/drivers/media/video/v4l2-ctrls.c index b6a2ee71e5c3..9abd9abd4502 100644 --- a/trunk/drivers/media/video/v4l2-ctrls.c +++ b/trunk/drivers/media/video/v4l2-ctrls.c @@ -755,7 +755,6 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_HUE_AUTO: case V4L2_CID_CHROMA_AGC: case V4L2_CID_COLOR_KILLER: - case V4L2_CID_AUTOBRIGHTNESS: case V4L2_CID_MPEG_AUDIO_MUTE: case V4L2_CID_MPEG_VIDEO_MUTE: case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: @@ -2121,7 +2120,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, /* First zero the helper field in the master control references */ for (i = 0; i < cs->count; i++) - helpers[i].mref->helper = NULL; + helpers[i].mref->helper = 0; for (i = 0, h = helpers; i < cs->count; i++, h++) { struct v4l2_ctrl_ref *mref = h->mref; diff --git a/trunk/drivers/media/video/v4l2-dev.c b/trunk/drivers/media/video/v4l2-dev.c index 07aeafca9eaa..af70f931727c 100644 --- a/trunk/drivers/media/video/v4l2-dev.c +++ b/trunk/drivers/media/video/v4l2-dev.c @@ -478,12 +478,12 @@ static int v4l2_open(struct inode *inode, struct file *filp) } err: - if (vdev->debug) - printk(KERN_DEBUG "%s: open (%d)\n", - video_device_node_name(vdev), ret); /* decrease the refcount in case of an error */ if (ret) video_put(vdev); + if (vdev->debug) + printk(KERN_DEBUG "%s: open (%d)\n", + video_device_node_name(vdev), ret); return ret; } @@ -500,12 +500,12 @@ static int v4l2_release(struct inode *inode, struct file *filp) if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); } - if (vdev->debug) - printk(KERN_DEBUG "%s: release\n", - video_device_node_name(vdev)); /* decrease the refcount unconditionally since the release() return value is ignored. */ video_put(vdev); + if (vdev->debug) + printk(KERN_DEBUG "%s: release\n", + video_device_node_name(vdev)); return ret; } @@ -697,8 +697,7 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd); SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd); SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd); - if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER && - (ops->vidioc_g_std || vdev->tvnorms))) + if (ops->vidioc_g_parm || vdev->vfl_type == VFL_TYPE_GRABBER) set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner); @@ -730,8 +729,6 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); - if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) - set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls, BASE_VIDIOC_PRIVATE); } diff --git a/trunk/drivers/media/video/v4l2-ioctl.c b/trunk/drivers/media/video/v4l2-ioctl.c index c3b7b5f59b32..70e0efb127a6 100644 --- a/trunk/drivers/media/video/v4l2-ioctl.c +++ b/trunk/drivers/media/video/v4l2-ioctl.c @@ -825,17 +825,6 @@ static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only) p->service_lines[1][i]); } -static void v4l_print_freq_band(const void *arg, bool write_only) -{ - const struct v4l2_frequency_band *p = arg; - - pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, " - "rangelow=%u, rangehigh=%u, modulation=0x%x\n", - p->tuner, p->type, p->index, - p->capability, p->rangelow, - p->rangehigh, p->modulation); -} - static void v4l_print_u32(const void *arg, bool write_only) { pr_cont("value=%u\n", *(const u32 *)arg); @@ -1256,14 +1245,10 @@ static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_tuner *p = arg; - int err; p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - err = ops->vidioc_g_tuner(file, fh, p); - if (!err) - p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; - return err; + return ops->vidioc_g_tuner(file, fh, p); } static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, @@ -1277,18 +1262,6 @@ static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, return ops->vidioc_s_tuner(file, fh, p); } -static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_modulator *p = arg; - int err; - - err = ops->vidioc_g_modulator(file, fh, p); - if (!err) - p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; - return err; -} - static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1513,8 +1486,7 @@ static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_queryctrl *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + struct v4l2_fh *vfh = fh; if (vfh && vfh->ctrl_handler) return v4l2_queryctrl(vfh->ctrl_handler, p); @@ -1530,8 +1502,7 @@ static int v4l_querymenu(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_querymenu *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + struct v4l2_fh *vfh = fh; if (vfh && vfh->ctrl_handler) return v4l2_querymenu(vfh->ctrl_handler, p); @@ -1547,8 +1518,7 @@ static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_control *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + struct v4l2_fh *vfh = fh; struct v4l2_ext_controls ctrls; struct v4l2_ext_control ctrl; @@ -1581,8 +1551,7 @@ static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_control *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + struct v4l2_fh *vfh = fh; struct v4l2_ext_controls ctrls; struct v4l2_ext_control ctrl; @@ -1610,8 +1579,7 @@ static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_ext_controls *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + struct v4l2_fh *vfh = fh; p->error_idx = p->count; if (vfh && vfh->ctrl_handler) @@ -1629,8 +1597,7 @@ static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_ext_controls *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + struct v4l2_fh *vfh = fh; p->error_idx = p->count; if (vfh && vfh->ctrl_handler) @@ -1648,8 +1615,7 @@ static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_ext_controls *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + struct v4l2_fh *vfh = fh; p->error_idx = p->count; if (vfh && vfh->ctrl_handler) @@ -1832,57 +1798,6 @@ static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops, return ops->vidioc_g_sliced_vbi_cap(file, fh, p); } -static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_frequency_band *p = arg; - enum v4l2_tuner_type type; - int err; - - type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - - if (type != p->type) - return -EINVAL; - if (ops->vidioc_enum_freq_bands) - return ops->vidioc_enum_freq_bands(file, fh, p); - if (ops->vidioc_g_tuner) { - struct v4l2_tuner t = { - .index = p->tuner, - .type = type, - }; - - err = ops->vidioc_g_tuner(file, fh, &t); - if (err) - return err; - p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS; - p->rangelow = t.rangelow; - p->rangehigh = t.rangehigh; - p->modulation = (type == V4L2_TUNER_RADIO) ? - V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; - return 0; - } - if (ops->vidioc_g_modulator) { - struct v4l2_modulator m = { - .index = p->tuner, - }; - - if (type != V4L2_TUNER_RADIO) - return -EINVAL; - err = ops->vidioc_g_modulator(file, fh, &m); - if (err) - return err; - p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS; - p->rangelow = m.rangelow; - p->rangehigh = m.rangehigh; - p->modulation = (type == V4L2_TUNER_RADIO) ? - V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; - return 0; - } - return -ENOTTY; -} - struct v4l2_ioctl_info { unsigned int ioctl; u32 flags; @@ -1891,7 +1806,7 @@ struct v4l2_ioctl_info { u32 offset; int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *p); - } u; + }; void (*debug)(const void *arg, bool write_only); }; @@ -1916,7 +1831,7 @@ struct v4l2_ioctl_info { .ioctl = _ioctl, \ .flags = _flags | INFO_FL_STD, \ .name = #_ioctl, \ - .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \ + .offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \ .debug = _debug, \ } @@ -1925,7 +1840,7 @@ struct v4l2_ioctl_info { .ioctl = _ioctl, \ .flags = _flags | INFO_FL_FUNC, \ .name = #_ioctl, \ - .u.func = _func, \ + .func = _func, \ .debug = _debug, \ } @@ -1964,7 +1879,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0), IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), + IOCTL_INFO_STD(VIDIOC_G_MODULATOR, vidioc_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), @@ -1985,7 +1900,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0), IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL), - IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), + IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, 0), IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)), IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)), IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0), @@ -2010,8 +1925,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), - IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), - IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), + IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, 0), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -2124,11 +2038,11 @@ static long __video_do_ioctl(struct file *file, if (info->flags & INFO_FL_STD) { typedef int (*vidioc_op)(struct file *file, void *fh, void *p); const void *p = vfd->ioctl_ops; - const vidioc_op *vidioc = p + info->u.offset; + const vidioc_op *vidioc = p + info->offset; ret = (*vidioc)(file, fh, arg); } else if (info->flags & INFO_FL_FUNC) { - ret = info->u.func(ops, file, fh, arg); + ret = info->func(ops, file, fh, arg); } else if (!ops->vidioc_default) { ret = -ENOTTY; } else { diff --git a/trunk/drivers/media/video/v4l2-mem2mem.c b/trunk/drivers/media/video/v4l2-mem2mem.c index 97b48318aee1..975d0fa938c6 100644 --- a/trunk/drivers/media/video/v4l2-mem2mem.c +++ b/trunk/drivers/media/video/v4l2-mem2mem.c @@ -19,9 +19,6 @@ #include #include -#include -#include -#include MODULE_DESCRIPTION("Mem to mem device framework for videobuf"); MODULE_AUTHOR("Pawel Osciak, "); @@ -410,24 +407,11 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff); unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct poll_table_struct *wait) { - struct video_device *vfd = video_devdata(file); - unsigned long req_events = poll_requested_events(wait); struct vb2_queue *src_q, *dst_q; struct vb2_buffer *src_vb = NULL, *dst_vb = NULL; unsigned int rc = 0; unsigned long flags; - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { - struct v4l2_fh *fh = file->private_data; - - if (v4l2_event_pending(fh)) - rc = POLLPRI; - else if (req_events & POLLPRI) - poll_wait(file, &fh->wait, wait); - if (!(req_events & (POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM))) - return rc; - } - src_q = v4l2_m2m_get_src_vq(m2m_ctx); dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); @@ -438,7 +422,7 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, */ if ((!src_q->streaming || list_empty(&src_q->queued_list)) && (!dst_q->streaming || list_empty(&dst_q->queued_list))) { - rc |= POLLERR; + rc = POLLERR; goto end; } diff --git a/trunk/drivers/media/video/via-camera.c b/trunk/drivers/media/video/via-camera.c index eb404c2ce270..308e150a39bc 100644 --- a/trunk/drivers/media/video/via-camera.c +++ b/trunk/drivers/media/video/via-camera.c @@ -963,7 +963,7 @@ static int viacam_do_try_fmt(struct via_camera *cam, upix->pixelformat = f->pixelformat; viacam_fmt_pre(upix, spix); - v4l2_fill_mbus_format(&mbus_fmt, spix, f->mbus_code); + v4l2_fill_mbus_format(&mbus_fmt, upix, f->mbus_code); ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt); v4l2_fill_pix_format(spix, &mbus_fmt); viacam_fmt_post(upix, spix); diff --git a/trunk/drivers/media/video/videobuf-dma-contig.c b/trunk/drivers/media/video/videobuf-dma-contig.c index 3a43ba0959bf..9b9a06fdd0f0 100644 --- a/trunk/drivers/media/video/videobuf-dma-contig.c +++ b/trunk/drivers/media/video/videobuf-dma-contig.c @@ -56,7 +56,7 @@ static int __videobuf_dc_alloc(struct device *dev, dev_err(dev, "dma_map_single failed\n"); free_pages_exact(mem->vaddr, mem->size); - mem->vaddr = NULL; + mem->vaddr = 0; return err; } } @@ -359,43 +359,32 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, size = vma->vm_end - vma->vm_start; size = (size < mem->size) ? size : mem->size; - if (!mem->cached) { + if (!mem->cached) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, - size, vma->vm_page_prot); + + pos = (unsigned long)mem->vaddr; + + while (size > 0) { + page = virt_to_page((void *)pos); + if (NULL == page) { + dev_err(q->dev, "mmap: virt_to_page failed\n"); + __videobuf_dc_free(q->dev, mem); + goto error; + } + retval = vm_insert_page(vma, start, page); if (retval) { - dev_err(q->dev, "mmap: remap failed with error %d. ", - retval); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); + dev_err(q->dev, "mmap: insert failed with error %d\n", + retval); + __videobuf_dc_free(q->dev, mem); goto error; } - } else { - pos = (unsigned long)mem->vaddr; + start += PAGE_SIZE; + pos += PAGE_SIZE; - while (size > 0) { - page = virt_to_page((void *)pos); - if (NULL == page) { - dev_err(q->dev, "mmap: virt_to_page failed\n"); - __videobuf_dc_free(q->dev, mem); - goto error; - } - retval = vm_insert_page(vma, start, page); - if (retval) { - dev_err(q->dev, "mmap: insert failed with error %d\n", - retval); - __videobuf_dc_free(q->dev, mem); - goto error; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } vma->vm_ops = &videobuf_vm_ops; diff --git a/trunk/drivers/media/video/videobuf2-core.c b/trunk/drivers/media/video/videobuf2-core.c index 268c7dd4f823..4e0290ab5071 100644 --- a/trunk/drivers/media/video/videobuf2-core.c +++ b/trunk/drivers/media/video/videobuf2-core.c @@ -715,8 +715,8 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create } /** - * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the - * memory and type values. + * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and + * type values. * @q: videobuf2 queue * @create: creation parameters, passed from userspace to vidioc_create_bufs * handler in driver diff --git a/trunk/drivers/media/video/vivi.c b/trunk/drivers/media/video/vivi.c index a05494b71b20..1e8c4f3ab602 100644 --- a/trunk/drivers/media/video/vivi.c +++ b/trunk/drivers/media/video/vivi.c @@ -230,6 +230,7 @@ struct vivi_dev { struct vivi_fmt *fmt; unsigned int width, height; struct vb2_queue vb_vidq; + enum v4l2_field field; unsigned int field_count; u8 bars[9][3]; @@ -622,7 +623,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->mv_count += 2; - buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + buf->vb.v4l2_buf.field = dev->field; dev->field_count++; buf->vb.v4l2_buf.sequence = dev->field_count >> 1; do_gettimeofday(&ts); @@ -924,7 +925,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.field = dev->field; f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = (f->fmt.pix.width * dev->fmt->depth) >> 3; @@ -943,16 +944,25 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct vivi_dev *dev = video_drvdata(file); struct vivi_fmt *fmt; + enum v4l2_field field; fmt = get_format(f); if (!fmt) { - dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n", + dprintk(dev, 1, "Fourcc format (0x%08x) invalid.\n", f->fmt.pix.pixelformat); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - fmt = get_format(f); + return -EINVAL; + } + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) { + field = V4L2_FIELD_INTERLACED; + } else if (V4L2_FIELD_INTERLACED != field) { + dprintk(dev, 1, "Field type invalid.\n"); + return -EINVAL; } - f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.field = field; v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2, &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0); f->fmt.pix.bytesperline = @@ -986,6 +996,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, dev->pixelsize = dev->fmt->depth / 8; dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; + dev->field = f->fmt.pix.field; return 0; } @@ -1319,6 +1330,9 @@ static int __init vivi_create_instance(int inst) /* Now that everything is fine, let's add it to device list */ list_add_tail(&dev->vivi_devlist, &vivi_devlist); + if (video_nr != -1) + video_nr++; + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", video_device_node_name(vfd)); return 0; diff --git a/trunk/drivers/mfd/ab3100-core.c b/trunk/drivers/mfd/ab3100-core.c index 78fca2902c8d..4276aab4f196 100644 --- a/trunk/drivers/mfd/ab3100-core.c +++ b/trunk/drivers/mfd/ab3100-core.c @@ -409,6 +409,8 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data) u32 fatevent; int err; + add_interrupt_randomness(irq); + err = ab3100_get_register_page_interruptible(ab3100, AB3100_EVENTA1, event_regs, 3); if (err) @@ -934,6 +936,8 @@ static int __devinit ab3100_probe(struct i2c_client *client, IRQF_ONESHOT, "ab3100-core", ab3100); if (err) goto exit_no_irq; + /* This real unpredictable IRQ is of course sampled for entropy */ + rand_initialize_irq(client->irq); err = abx500_register_ops(&client->dev, &ab3100_ops); if (err) diff --git a/trunk/drivers/mfd/tps65010.c b/trunk/drivers/mfd/tps65010.c index da2691f22e11..93d5fdf020c7 100644 --- a/trunk/drivers/mfd/tps65010.c +++ b/trunk/drivers/mfd/tps65010.c @@ -563,7 +563,8 @@ static int tps65010_probe(struct i2c_client *client, */ if (client->irq > 0) { status = request_irq(client->irq, tps65010_irq, - IRQF_TRIGGER_FALLING, DRIVER_NAME, tps); + IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING, + DRIVER_NAME, tps); if (status < 0) { dev_dbg(&client->dev, "can't get IRQ %d, err %d\n", client->irq, status); diff --git a/trunk/drivers/mfd/twl-core.c b/trunk/drivers/mfd/twl-core.c index 1c32afed28aa..b012efd29e01 100644 --- a/trunk/drivers/mfd/twl-core.c +++ b/trunk/drivers/mfd/twl-core.c @@ -717,9 +717,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, static struct regulator_consumer_supply usb1v8 = { .supply = "usb1v8", }; - static struct regulator_consumer_supply usb3v1[] = { - { .supply = "usb3v1" }, - { .supply = "bci3v1" }, + static struct regulator_consumer_supply usb3v1 = { + .supply = "usb3v1", }; /* First add the regulators so that they can be used by transceiver */ @@ -747,7 +746,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, return PTR_ERR(child); child = add_regulator_linked(TWL4030_REG_VUSB3V1, - &usb_fixed, usb3v1, 2, + &usb_fixed, &usb3v1, 1, features); if (IS_ERR(child)) return PTR_ERR(child); @@ -768,7 +767,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (twl_has_regulator() && child) { usb1v5.dev_name = dev_name(child); usb1v8.dev_name = dev_name(child); - usb3v1[0].dev_name = dev_name(child); + usb3v1.dev_name = dev_name(child); } } if (twl_has_usb() && pdata->usb && twl_class_is_6030()) { diff --git a/trunk/drivers/mfd/wm831x-otp.c b/trunk/drivers/mfd/wm831x-otp.c index b90f3e06b6c9..f742745ff354 100644 --- a/trunk/drivers/mfd/wm831x-otp.c +++ b/trunk/drivers/mfd/wm831x-otp.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -67,7 +66,6 @@ static DEVICE_ATTR(unique_id, 0444, wm831x_unique_id_show, NULL); int wm831x_otp_init(struct wm831x *wm831x) { - char uuid[WM831X_UNIQUE_ID_LEN]; int ret; ret = device_create_file(wm831x->dev, &dev_attr_unique_id); @@ -75,12 +73,6 @@ int wm831x_otp_init(struct wm831x *wm831x) dev_err(wm831x->dev, "Unique ID attribute not created: %d\n", ret); - ret = wm831x_unique_id_read(wm831x, uuid); - if (ret == 0) - add_device_randomness(uuid, sizeof(uuid)); - else - dev_err(wm831x->dev, "Failed to read UUID: %d\n", ret); - return ret; } diff --git a/trunk/drivers/mmc/host/omap.c b/trunk/drivers/mmc/host/omap.c index 50e08f03aa65..3e8dcf8d2e05 100644 --- a/trunk/drivers/mmc/host/omap.c +++ b/trunk/drivers/mmc/host/omap.c @@ -17,12 +17,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include @@ -130,10 +128,6 @@ struct mmc_omap_host { unsigned char id; /* 16xx chips have 2 MMC blocks */ struct clk * iclk; struct clk * fclk; - struct dma_chan *dma_rx; - u32 dma_rx_burst; - struct dma_chan *dma_tx; - u32 dma_tx_burst; struct resource *mem_res; void __iomem *virt_base; unsigned int phys_base; @@ -159,8 +153,12 @@ struct mmc_omap_host { unsigned use_dma:1; unsigned brs_received:1, dma_done:1; + unsigned dma_is_read:1; unsigned dma_in_use:1; + int dma_ch; spinlock_t dma_lock; + struct timer_list dma_timer; + unsigned dma_len; struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS]; struct mmc_omap_slot *current_slot; @@ -408,25 +406,18 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data, int abort) { enum dma_data_direction dma_data_dir; - struct device *dev = mmc_dev(host->mmc); - struct dma_chan *c; - if (data->flags & MMC_DATA_WRITE) { + BUG_ON(host->dma_ch < 0); + if (data->error) + omap_stop_dma(host->dma_ch); + /* Release DMA channel lazily */ + mod_timer(&host->dma_timer, jiffies + HZ); + if (data->flags & MMC_DATA_WRITE) dma_data_dir = DMA_TO_DEVICE; - c = host->dma_tx; - } else { + else dma_data_dir = DMA_FROM_DEVICE; - c = host->dma_rx; - } - if (c) { - if (data->error) { - dmaengine_terminate_all(c); - /* Claim nothing transferred on error... */ - data->bytes_xfered = 0; - } - dev = c->device->dev; - } - dma_unmap_sg(dev, data->sg, host->sg_len, dma_data_dir); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, + dma_data_dir); } static void mmc_omap_send_stop_work(struct work_struct *work) @@ -533,6 +524,16 @@ mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data) mmc_omap_xfer_done(host, data); } +static void +mmc_omap_dma_timer(unsigned long data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + + BUG_ON(host->dma_ch < 0); + omap_free_dma(host->dma_ch); + host->dma_ch = -1; +} + static void mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data) { @@ -890,15 +891,159 @@ static void mmc_omap_cover_handler(unsigned long param) jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY)); } -static void mmc_omap_dma_callback(void *priv) +/* Prepare to transfer the next segment of a scatterlist */ +static void +mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) { - struct mmc_omap_host *host = priv; - struct mmc_data *data = host->data; + int dma_ch = host->dma_ch; + unsigned long data_addr; + u16 buf, frame; + u32 count; + struct scatterlist *sg = &data->sg[host->sg_idx]; + int src_port = 0; + int dst_port = 0; + int sync_dev = 0; + + data_addr = host->phys_base + OMAP_MMC_REG(host, DATA); + frame = data->blksz; + count = sg_dma_len(sg); + + if ((data->blocks == 1) && (count > data->blksz)) + count = frame; + + host->dma_len = count; + + /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx. + * Use 16 or 32 word frames when the blocksize is at least that large. + * Blocksize is usually 512 bytes; but not for some SD reads. + */ + if (cpu_is_omap15xx() && frame > 32) + frame = 32; + else if (frame > 64) + frame = 64; + count /= frame; + frame >>= 1; + + if (!(data->flags & MMC_DATA_WRITE)) { + buf = 0x800f | ((frame - 1) << 8); + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_TIPB; + dst_port = OMAP_DMA_PORT_EMIFF; + } + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_MMC1_RX; + + omap_set_dma_src_params(dma_ch, src_port, + OMAP_DMA_AMODE_CONSTANT, + data_addr, 0, 0); + omap_set_dma_dest_params(dma_ch, dst_port, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sg), 0, 0); + omap_set_dma_dest_data_pack(dma_ch, 1); + omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + } else { + buf = 0x0f80 | ((frame - 1) << 0); + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_EMIFF; + dst_port = OMAP_DMA_PORT_TIPB; + } + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_MMC1_TX; + + omap_set_dma_dest_params(dma_ch, dst_port, + OMAP_DMA_AMODE_CONSTANT, + data_addr, 0, 0); + omap_set_dma_src_params(dma_ch, src_port, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sg), 0, 0); + omap_set_dma_src_data_pack(dma_ch, 1); + omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + } - /* If we got to the end of DMA, assume everything went well */ - data->bytes_xfered += data->blocks * data->blksz; + /* Max limit for DMA frame count is 0xffff */ + BUG_ON(count > 0xffff); - mmc_omap_dma_done(host, data); + OMAP_MMC_WRITE(host, BUF, buf); + omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, + frame, count, OMAP_DMA_SYNC_FRAME, + sync_dev, 0); +} + +/* A scatterlist segment completed */ +static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + struct mmc_data *mmcdat = host->data; + + if (unlikely(host->dma_ch < 0)) { + dev_err(mmc_dev(host->mmc), + "DMA callback while DMA not enabled\n"); + return; + } + /* FIXME: We really should do something to _handle_ the errors */ + if (ch_status & OMAP1_DMA_TOUT_IRQ) { + dev_err(mmc_dev(host->mmc),"DMA timeout\n"); + return; + } + if (ch_status & OMAP_DMA_DROP_IRQ) { + dev_err(mmc_dev(host->mmc), "DMA sync error\n"); + return; + } + if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { + return; + } + mmcdat->bytes_xfered += host->dma_len; + host->sg_idx++; + if (host->sg_idx < host->sg_len) { + mmc_omap_prepare_dma(host, host->data); + omap_start_dma(host->dma_ch); + } else + mmc_omap_dma_done(host, host->data); +} + +static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data) +{ + const char *dma_dev_name; + int sync_dev, dma_ch, is_read, r; + + is_read = !(data->flags & MMC_DATA_WRITE); + del_timer_sync(&host->dma_timer); + if (host->dma_ch >= 0) { + if (is_read == host->dma_is_read) + return 0; + omap_free_dma(host->dma_ch); + host->dma_ch = -1; + } + + if (is_read) { + if (host->id == 0) { + sync_dev = OMAP_DMA_MMC_RX; + dma_dev_name = "MMC1 read"; + } else { + sync_dev = OMAP_DMA_MMC2_RX; + dma_dev_name = "MMC2 read"; + } + } else { + if (host->id == 0) { + sync_dev = OMAP_DMA_MMC_TX; + dma_dev_name = "MMC1 write"; + } else { + sync_dev = OMAP_DMA_MMC2_TX; + dma_dev_name = "MMC2 write"; + } + } + r = omap_request_dma(sync_dev, dma_dev_name, mmc_omap_dma_cb, + host, &dma_ch); + if (r != 0) { + dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r); + return r; + } + host->dma_ch = dma_ch; + host->dma_is_read = is_read; + + return 0; } static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req) @@ -973,85 +1118,33 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) host->sg_idx = 0; if (use_dma) { - enum dma_data_direction dma_data_dir; - struct dma_async_tx_descriptor *tx; - struct dma_chan *c; - u32 burst, *bp; - u16 buf; - - /* - * FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx - * and 24xx. Use 16 or 32 word frames when the - * blocksize is at least that large. Blocksize is - * usually 512 bytes; but not for some SD reads. - */ - burst = cpu_is_omap15xx() ? 32 : 64; - if (burst > data->blksz) - burst = data->blksz; - - burst >>= 1; - - if (data->flags & MMC_DATA_WRITE) { - c = host->dma_tx; - bp = &host->dma_tx_burst; - buf = 0x0f80 | (burst - 1) << 0; - dma_data_dir = DMA_TO_DEVICE; - } else { - c = host->dma_rx; - bp = &host->dma_rx_burst; - buf = 0x800f | (burst - 1) << 8; - dma_data_dir = DMA_FROM_DEVICE; - } - - if (!c) - goto use_pio; - - /* Only reconfigure if we have a different burst size */ - if (*bp != burst) { - struct dma_slave_config cfg; - - cfg.src_addr = host->phys_base + OMAP_MMC_REG(host, DATA); - cfg.dst_addr = host->phys_base + OMAP_MMC_REG(host, DATA); - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - cfg.src_maxburst = burst; - cfg.dst_maxburst = burst; - - if (dmaengine_slave_config(c, &cfg)) - goto use_pio; - - *bp = burst; - } - - host->sg_len = dma_map_sg(c->device->dev, data->sg, sg_len, - dma_data_dir); - if (host->sg_len == 0) - goto use_pio; - - tx = dmaengine_prep_slave_sg(c, data->sg, host->sg_len, - data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!tx) - goto use_pio; - - OMAP_MMC_WRITE(host, BUF, buf); - - tx->callback = mmc_omap_dma_callback; - tx->callback_param = host; - dmaengine_submit(tx); - host->brs_received = 0; - host->dma_done = 0; - host->dma_in_use = 1; - return; + if (mmc_omap_get_dma_channel(host, data) == 0) { + enum dma_data_direction dma_data_dir; + + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + + host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, + sg_len, dma_data_dir); + host->total_bytes_left = 0; + mmc_omap_prepare_dma(host, req->data); + host->brs_received = 0; + host->dma_done = 0; + host->dma_in_use = 1; + } else + use_dma = 0; } - use_pio: /* Revert to PIO? */ - OMAP_MMC_WRITE(host, BUF, 0x1f1f); - host->total_bytes_left = data->blocks * block_size; - host->sg_len = sg_len; - mmc_omap_sg_to_buf(host); - host->dma_in_use = 0; + if (!use_dma) { + OMAP_MMC_WRITE(host, BUF, 0x1f1f); + host->total_bytes_left = data->blocks * block_size; + host->sg_len = sg_len; + mmc_omap_sg_to_buf(host); + host->dma_in_use = 0; + } } static void mmc_omap_start_request(struct mmc_omap_host *host, @@ -1064,12 +1157,8 @@ static void mmc_omap_start_request(struct mmc_omap_host *host, /* only touch fifo AFTER the controller readies it */ mmc_omap_prepare_data(host, req); mmc_omap_start_command(host, req->cmd); - if (host->dma_in_use) { - struct dma_chan *c = host->data->flags & MMC_DATA_WRITE ? - host->dma_tx : host->dma_rx; - - dma_async_issue_pending(c); - } + if (host->dma_in_use) + omap_start_dma(host->dma_ch); } static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) @@ -1311,8 +1400,6 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_omap_host *host = NULL; struct resource *res; - dma_cap_mask_t mask; - unsigned sig; int i, ret = 0; int irq; @@ -1352,6 +1439,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host); spin_lock_init(&host->dma_lock); + setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host); spin_lock_init(&host->slot_lock); init_waitqueue_head(&host->slot_wq); @@ -1362,7 +1450,11 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) host->id = pdev->id; host->mem_res = res; host->irq = irq; + host->use_dma = 1; + host->dev->dma_mask = &pdata->dma_mask; + host->dma_ch = -1; + host->irq = irq; host->phys_base = host->mem_res->start; host->virt_base = ioremap(res->start, resource_size(res)); @@ -1382,48 +1474,9 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) goto err_free_iclk; } - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - host->dma_tx_burst = -1; - host->dma_rx_burst = -1; - - if (cpu_is_omap24xx()) - sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX; - else - sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX; - host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); -#if 0 - if (!host->dma_tx) { - dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n", - sig); - goto err_dma; - } -#else - if (!host->dma_tx) - dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n", - sig); -#endif - if (cpu_is_omap24xx()) - sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX; - else - sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX; - host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); -#if 0 - if (!host->dma_rx) { - dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n", - sig); - goto err_dma; - } -#else - if (!host->dma_rx) - dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n", - sig); -#endif - ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); if (ret) - goto err_free_dma; + goto err_free_fclk; if (pdata->init != NULL) { ret = pdata->init(&pdev->dev); @@ -1457,11 +1510,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) pdata->cleanup(&pdev->dev); err_free_irq: free_irq(host->irq, host); -err_free_dma: - if (host->dma_tx) - dma_release_channel(host->dma_tx); - if (host->dma_rx) - dma_release_channel(host->dma_rx); +err_free_fclk: clk_put(host->fclk); err_free_iclk: clk_disable(host->iclk); @@ -1496,11 +1545,6 @@ static int __devexit mmc_omap_remove(struct platform_device *pdev) clk_disable(host->iclk); clk_put(host->iclk); - if (host->dma_tx) - dma_release_channel(host->dma_tx); - if (host->dma_rx) - dma_release_channel(host->dma_rx); - iounmap(host->virt_base); release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); diff --git a/trunk/drivers/mmc/host/omap_hsmmc.c b/trunk/drivers/mmc/host/omap_hsmmc.c index 3a09f93cc3b6..bc28627af66b 100644 --- a/trunk/drivers/mmc/host/omap_hsmmc.c +++ b/trunk/drivers/mmc/host/omap_hsmmc.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -30,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -39,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -167,8 +166,7 @@ struct omap_hsmmc_host { int suspended; int irq; int use_dma, dma_ch; - struct dma_chan *tx_chan; - struct dma_chan *rx_chan; + int dma_line_tx, dma_line_rx; int slot_id; int response_busy; int context_loss; @@ -799,12 +797,6 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data) return DMA_FROM_DEVICE; } -static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host, - struct mmc_data *data) -{ - return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan; -} - static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq) { int dma_ch; @@ -897,13 +889,10 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno) spin_unlock_irqrestore(&host->irq_lock, flags); if (host->use_dma && dma_ch != -1) { - struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data); - - dmaengine_terminate_all(chan); - dma_unmap_sg(chan->device->dev, - host->data->sg, host->data->sg_len, + dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, + host->data->sg_len, omap_hsmmc_get_dma_dir(host, host->data)); - + omap_free_dma(dma_ch); host->data->host_cookie = 0; } host->data = NULL; @@ -1201,29 +1190,90 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) return IRQ_HANDLED; } -static void omap_hsmmc_dma_callback(void *param) +static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host, + struct mmc_data *data) +{ + int sync_dev; + + if (data->flags & MMC_DATA_WRITE) + sync_dev = host->dma_line_tx; + else + sync_dev = host->dma_line_rx; + return sync_dev; +} + +static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host, + struct mmc_data *data, + struct scatterlist *sgl) +{ + int blksz, nblk, dma_ch; + + dma_ch = host->dma_ch; + if (data->flags & MMC_DATA_WRITE) { + omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sgl), 0, 0); + } else { + omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sgl), 0, 0); + } + + blksz = host->data->blksz; + nblk = sg_dma_len(sgl) / blksz; + + omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, + blksz / 4, nblk, OMAP_DMA_SYNC_FRAME, + omap_hsmmc_get_dma_sync_dev(host, data), + !(data->flags & MMC_DATA_WRITE)); + + omap_start_dma(dma_ch); +} + +/* + * DMA call back function + */ +static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) { - struct omap_hsmmc_host *host = param; - struct dma_chan *chan; + struct omap_hsmmc_host *host = cb_data; struct mmc_data *data; - int req_in_progress; + int dma_ch, req_in_progress; + unsigned long flags; + + if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { + dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n", + ch_status); + return; + } - spin_lock_irq(&host->irq_lock); + spin_lock_irqsave(&host->irq_lock, flags); if (host->dma_ch < 0) { - spin_unlock_irq(&host->irq_lock); + spin_unlock_irqrestore(&host->irq_lock, flags); return; } data = host->mrq->data; - chan = omap_hsmmc_get_dma_chan(host, data); + host->dma_sg_idx++; + if (host->dma_sg_idx < host->dma_len) { + /* Fire up the next transfer. */ + omap_hsmmc_config_dma_params(host, data, + data->sg + host->dma_sg_idx); + spin_unlock_irqrestore(&host->irq_lock, flags); + return; + } + if (!data->host_cookie) - dma_unmap_sg(chan->device->dev, - data->sg, data->sg_len, + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, omap_hsmmc_get_dma_dir(host, data)); req_in_progress = host->req_in_progress; + dma_ch = host->dma_ch; host->dma_ch = -1; - spin_unlock_irq(&host->irq_lock); + spin_unlock_irqrestore(&host->irq_lock, flags); + + omap_free_dma(dma_ch); /* If DMA has finished after TC, complete the request */ if (!req_in_progress) { @@ -1236,8 +1286,7 @@ static void omap_hsmmc_dma_callback(void *param) static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, struct mmc_data *data, - struct omap_hsmmc_next *next, - struct dma_chan *chan) + struct omap_hsmmc_next *next) { int dma_len; @@ -1252,7 +1301,8 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, /* Check if next job is already prepared */ if (next || (!next && data->host_cookie != host->next_data.cookie)) { - dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len, + dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, omap_hsmmc_get_dma_dir(host, data)); } else { @@ -1279,11 +1329,8 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, struct mmc_request *req) { - struct dma_slave_config cfg; - struct dma_async_tx_descriptor *tx; - int ret = 0, i; + int dma_ch = 0, ret = 0, i; struct mmc_data *data = req->data; - struct dma_chan *chan; /* Sanity check: all the SG entries must be aligned by block size. */ for (i = 0; i < data->sg_len; i++) { @@ -1301,41 +1348,22 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, BUG_ON(host->dma_ch != -1); - chan = omap_hsmmc_get_dma_chan(host, data); - - cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA; - cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.src_maxburst = data->blksz / 4; - cfg.dst_maxburst = data->blksz / 4; - - ret = dmaengine_slave_config(chan, &cfg); - if (ret) + ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data), + "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch); + if (ret != 0) { + dev_err(mmc_dev(host->mmc), + "%s: omap_request_dma() failed with %d\n", + mmc_hostname(host->mmc), ret); return ret; - - ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan); + } + ret = omap_hsmmc_pre_dma_transfer(host, data, NULL); if (ret) return ret; - tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len, - data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!tx) { - dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n"); - /* FIXME: cleanup */ - return -1; - } - - tx->callback = omap_hsmmc_dma_callback; - tx->callback_param = host; + host->dma_ch = dma_ch; + host->dma_sg_idx = 0; - /* Does not fail */ - dmaengine_submit(tx); - - host->dma_ch = 1; - - dma_async_issue_pending(chan); + omap_hsmmc_config_dma_params(host, data, data->sg); return 0; } @@ -1417,11 +1445,11 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, struct omap_hsmmc_host *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (host->use_dma && data->host_cookie) { - struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data); - - dma_unmap_sg(c->device->dev, data->sg, data->sg_len, - omap_hsmmc_get_dma_dir(host, data)); + if (host->use_dma) { + if (data->host_cookie) + dma_unmap_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, + omap_hsmmc_get_dma_dir(host, data)); data->host_cookie = 0; } } @@ -1436,13 +1464,10 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, return ; } - if (host->use_dma) { - struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data); - + if (host->use_dma) if (omap_hsmmc_pre_dma_transfer(host, mrq->data, - &host->next_data, c)) + &host->next_data)) mrq->data->host_cookie = 0; - } } /* @@ -1775,8 +1800,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) struct resource *res; int ret, irq; const struct of_device_id *match; - dma_cap_mask_t mask; - unsigned tx_req, rx_req; match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev); if (match) { @@ -1821,6 +1844,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) host->pdata = pdata; host->dev = &pdev->dev; host->use_dma = 1; + host->dev->dma_mask = &pdata->dma_mask; host->dma_ch = -1; host->irq = irq; host->slot_id = 0; @@ -1910,7 +1934,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) ret = -ENXIO; goto err_irq; } - tx_req = res->start; + host->dma_line_tx = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); if (!res) { @@ -1918,24 +1942,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) ret = -ENXIO; goto err_irq; } - rx_req = res->start; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req); - if (!host->rx_chan) { - dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req); - ret = -ENXIO; - goto err_irq; - } - - host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req); - if (!host->tx_chan) { - dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req); - ret = -ENXIO; - goto err_irq; - } + host->dma_line_rx = res->start; /* Request IRQ for MMC operations */ ret = request_irq(host->irq, omap_hsmmc_irq, 0, @@ -2014,10 +2021,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) err_irq_cd_init: free_irq(host->irq, host); err_irq: - if (host->tx_chan) - dma_release_channel(host->tx_chan); - if (host->rx_chan) - dma_release_channel(host->rx_chan); pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); clk_put(host->fclk); @@ -2053,11 +2056,6 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev) if (mmc_slot(host).card_detect_irq) free_irq(mmc_slot(host).card_detect_irq, host); - if (host->tx_chan) - dma_release_channel(host->tx_chan); - if (host->rx_chan) - dma_release_channel(host->rx_chan); - pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); clk_put(host->fclk); diff --git a/trunk/drivers/mtd/nand/omap2.c b/trunk/drivers/mtd/nand/omap2.c index e9309b3659e7..d7f681d0c9b9 100644 --- a/trunk/drivers/mtd/nand/omap2.c +++ b/trunk/drivers/mtd/nand/omap2.c @@ -9,7 +9,6 @@ */ #include -#include #include #include #include @@ -19,7 +18,6 @@ #include #include #include -#include #include #include @@ -125,7 +123,7 @@ struct omap_nand_info { int gpmc_cs; unsigned long phys_base; struct completion comp; - struct dma_chan *dma; + int dma_ch; int gpmc_irq; enum { OMAP_NAND_IO_READ = 0, /* read */ @@ -338,10 +336,12 @@ static void omap_write_buf_pref(struct mtd_info *mtd, } /* - * omap_nand_dma_callback: callback on the completion of dma transfer + * omap_nand_dma_cb: callback on the completion of dma transfer + * @lch: logical channel + * @ch_satuts: channel status * @data: pointer to completion data structure */ -static void omap_nand_dma_callback(void *data) +static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) { complete((struct completion *) data); } @@ -358,13 +358,17 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - struct dma_async_tx_descriptor *tx; enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - struct scatterlist sg; - unsigned long tim, limit; - unsigned n; + dma_addr_t dma_addr; int ret; + unsigned long tim, limit; + + /* The fifo depth is 64 bytes max. + * But configure the FIFO-threahold to 32 to get a sync at each frame + * and frame length is 32 bytes. + */ + int buf_len = len >> 6; if (addr >= high_memory) { struct page *p1; @@ -378,33 +382,40 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK); } - sg_init_one(&sg, addr, len); - n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); - if (n == 0) { + dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir); + if (dma_mapping_error(&info->pdev->dev, dma_addr)) { dev_err(&info->pdev->dev, "Couldn't DMA map a %d byte buffer\n", len); goto out_copy; } - tx = dmaengine_prep_slave_sg(info->dma, &sg, n, - is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!tx) - goto out_copy_unmap; - - tx->callback = omap_nand_dma_callback; - tx->callback_param = &info->comp; - dmaengine_submit(tx); - - /* configure and start prefetch transfer */ + if (is_write) { + omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + info->phys_base, 0, 0); + omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + dma_addr, 0, 0); + omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, + 0x10, buf_len, OMAP_DMA_SYNC_FRAME, + OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC); + } else { + omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + info->phys_base, 0, 0); + omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + dma_addr, 0, 0); + omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, + 0x10, buf_len, OMAP_DMA_SYNC_FRAME, + OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC); + } + /* configure and start prefetch transfer */ ret = gpmc_prefetch_enable(info->gpmc_cs, - PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write); + PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write); if (ret) /* PFPW engine is busy, use cpu copy method */ goto out_copy_unmap; init_completion(&info->comp); - dma_async_issue_pending(info->dma); + + omap_start_dma(info->dma_ch); /* setup and start DMA using dma_addr */ wait_for_completion(&info->comp); @@ -416,11 +427,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, /* disable and stop the PFPW engine */ gpmc_prefetch_reset(info->gpmc_cs); - dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); + dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); return 0; out_copy_unmap: - dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); + dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); out_copy: if (info->nand.options & NAND_BUSWIDTH_16) is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) @@ -1153,8 +1164,6 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) struct omap_nand_platform_data *pdata; int err; int i, offset; - dma_cap_mask_t mask; - unsigned sig; pdata = pdev->dev.platform_data; if (pdata == NULL) { @@ -1235,31 +1244,18 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) break; case NAND_OMAP_PREFETCH_DMA: - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - sig = OMAP24XX_DMA_GPMC; - info->dma = dma_request_channel(mask, omap_dma_filter_fn, &sig); - if (!info->dma) { - dev_err(&pdev->dev, "DMA engine request failed\n"); - err = -ENXIO; + err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", + omap_nand_dma_cb, &info->comp, &info->dma_ch); + if (err < 0) { + info->dma_ch = -1; + dev_err(&pdev->dev, "DMA request failed!\n"); goto out_release_mem_region; } else { - struct dma_slave_config cfg; - int rc; - - memset(&cfg, 0, sizeof(cfg)); - cfg.src_addr = info->phys_base; - cfg.dst_addr = info->phys_base; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.src_maxburst = 16; - cfg.dst_maxburst = 16; - rc = dmaengine_slave_config(info->dma, &cfg); - if (rc) { - dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", - rc); - goto out_release_mem_region; - } + omap_set_dma_dest_burst_mode(info->dma_ch, + OMAP_DMA_DATA_BURST_16); + omap_set_dma_src_burst_mode(info->dma_ch, + OMAP_DMA_DATA_BURST_16); + info->nand.read_buf = omap_read_buf_dma_pref; info->nand.write_buf = omap_write_buf_dma_pref; } @@ -1362,8 +1358,6 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) return 0; out_release_mem_region: - if (info->dma) - dma_release_channel(info->dma); release_mem_region(info->phys_base, NAND_IO_SIZE); out_free_info: kfree(info); @@ -1379,8 +1373,8 @@ static int omap_nand_remove(struct platform_device *pdev) omap3_free_bch(&info->mtd); platform_set_drvdata(pdev, NULL); - if (info->dma) - dma_release_channel(info->dma); + if (info->dma_ch != -1) + omap_free_dma(info->dma_ch); if (info->gpmc_irq) free_irq(info->gpmc_irq, info); diff --git a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index dd451c3dd83d..9aaf863b4237 100644 --- a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9360,7 +9360,8 @@ static int __devinit bnx2x_prev_mark_path(struct bnx2x *bp) struct bnx2x_prev_path_list *tmp_list; int rc; - tmp_list = kmalloc(sizeof(struct bnx2x_prev_path_list), GFP_KERNEL); + tmp_list = (struct bnx2x_prev_path_list *) + kmalloc(sizeof(struct bnx2x_prev_path_list), GFP_KERNEL); if (!tmp_list) { BNX2X_ERR("Failed to allocate 'bnx2x_prev_path_list'\n"); return -ENOMEM; diff --git a/trunk/drivers/net/ethernet/broadcom/tg3.c b/trunk/drivers/net/ethernet/broadcom/tg3.c index bf906c51d82a..9a009fd6ea1b 100644 --- a/trunk/drivers/net/ethernet/broadcom/tg3.c +++ b/trunk/drivers/net/ethernet/broadcom/tg3.c @@ -92,7 +92,7 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits) #define DRV_MODULE_NAME "tg3" #define TG3_MAJ_NUM 3 -#define TG3_MIN_NUM 124 +#define TG3_MIN_NUM 123 #define DRV_MODULE_VERSION \ __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM) #define DRV_MODULE_RELDATE "March 21, 2012" @@ -672,12 +672,6 @@ static int tg3_ape_lock(struct tg3 *tp, int locknum) else bit = 1 << tp->pci_fn; break; - case TG3_APE_LOCK_PHY0: - case TG3_APE_LOCK_PHY1: - case TG3_APE_LOCK_PHY2: - case TG3_APE_LOCK_PHY3: - bit = APE_LOCK_REQ_DRIVER; - break; default: return -EINVAL; } @@ -729,12 +723,6 @@ static void tg3_ape_unlock(struct tg3 *tp, int locknum) else bit = 1 << tp->pci_fn; break; - case TG3_APE_LOCK_PHY0: - case TG3_APE_LOCK_PHY1: - case TG3_APE_LOCK_PHY2: - case TG3_APE_LOCK_PHY3: - bit = APE_LOCK_GRANT_DRIVER; - break; default: return; } @@ -1064,8 +1052,6 @@ static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) udelay(80); } - tg3_ape_lock(tp, tp->phy_ape_lock); - *val = 0x0; frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) & @@ -1100,8 +1086,6 @@ static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) udelay(80); } - tg3_ape_unlock(tp, tp->phy_ape_lock); - return ret; } @@ -1121,8 +1105,6 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val) udelay(80); } - tg3_ape_lock(tp, tp->phy_ape_lock); - frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) & MI_COM_PHY_ADDR_MASK); frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & @@ -1153,8 +1135,6 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val) udelay(80); } - tg3_ape_unlock(tp, tp->phy_ape_lock); - return ret; } @@ -9086,7 +9066,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 || tg3_flag(tp, 57765_PLUS)) { val = tr32(TG3_RDMA_RSRVCTRL_REG); - if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) { val &= ~(TG3_RDMA_RSRVCTRL_TXMRGN_MASK | TG3_RDMA_RSRVCTRL_FIFO_LWM_MASK | TG3_RDMA_RSRVCTRL_FIFO_HWM_MASK); @@ -9276,19 +9257,6 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) tw32_f(RDMAC_MODE, rdmac_mode); udelay(40); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) { - for (i = 0; i < TG3_NUM_RDMA_CHANNELS; i++) { - if (tr32(TG3_RDMA_LENGTH + (i << 2)) > TG3_MAX_MTU(tp)) - break; - } - if (i < TG3_NUM_RDMA_CHANNELS) { - val = tr32(TG3_LSO_RD_DMA_CRPTEN_CTRL); - val |= TG3_LSO_RD_DMA_TX_LENGTH_WA; - tw32(TG3_LSO_RD_DMA_CRPTEN_CTRL, val); - tg3_flag_set(tp, 5719_RDMA_BUG); - } - } - tw32(RCVDCC_MODE, RCVDCC_MODE_ENABLE | RCVDCC_MODE_ATTN_ENABLE); if (!tg3_flag(tp, 5705_PLUS)) tw32(MBFREE_MODE, MBFREE_MODE_ENABLE); @@ -9648,16 +9616,6 @@ static void tg3_periodic_fetch_stats(struct tg3 *tp) TG3_STAT_ADD32(&sp->tx_ucast_packets, MAC_TX_STATS_UCAST); TG3_STAT_ADD32(&sp->tx_mcast_packets, MAC_TX_STATS_MCAST); TG3_STAT_ADD32(&sp->tx_bcast_packets, MAC_TX_STATS_BCAST); - if (unlikely(tg3_flag(tp, 5719_RDMA_BUG) && - (sp->tx_ucast_packets.low + sp->tx_mcast_packets.low + - sp->tx_bcast_packets.low) > TG3_NUM_RDMA_CHANNELS)) { - u32 val; - - val = tr32(TG3_LSO_RD_DMA_CRPTEN_CTRL); - val &= ~TG3_LSO_RD_DMA_TX_LENGTH_WA; - tw32(TG3_LSO_RD_DMA_CRPTEN_CTRL, val); - tg3_flag_clear(tp, 5719_RDMA_BUG); - } TG3_STAT_ADD32(&sp->rx_octets, MAC_RX_STATS_OCTETS); TG3_STAT_ADD32(&sp->rx_fragments, MAC_RX_STATS_FRAGMENTS); @@ -12524,12 +12482,10 @@ static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev, { struct tg3 *tp = netdev_priv(dev); - spin_lock_bh(&tp->lock); - if (!tp->hw_stats) { - spin_unlock_bh(&tp->lock); + if (!tp->hw_stats) return &tp->net_stats_prev; - } + spin_lock_bh(&tp->lock); tg3_get_nstats(tp, stats); spin_unlock_bh(&tp->lock); @@ -13692,23 +13648,6 @@ static int __devinit tg3_phy_probe(struct tg3 *tp) tg3_flag_set(tp, PAUSE_AUTONEG); tp->link_config.flowctrl = FLOW_CTRL_TX | FLOW_CTRL_RX; - if (tg3_flag(tp, ENABLE_APE)) { - switch (tp->pci_fn) { - case 0: - tp->phy_ape_lock = TG3_APE_LOCK_PHY0; - break; - case 1: - tp->phy_ape_lock = TG3_APE_LOCK_PHY1; - break; - case 2: - tp->phy_ape_lock = TG3_APE_LOCK_PHY2; - break; - case 3: - tp->phy_ape_lock = TG3_APE_LOCK_PHY3; - break; - } - } - if (tg3_flag(tp, USE_PHYLIB)) return tg3_phy_init(tp); diff --git a/trunk/drivers/net/ethernet/broadcom/tg3.h b/trunk/drivers/net/ethernet/broadcom/tg3.h index 6d52cb286826..a1b75cd67b9d 100644 --- a/trunk/drivers/net/ethernet/broadcom/tg3.h +++ b/trunk/drivers/net/ethernet/broadcom/tg3.h @@ -1376,11 +1376,7 @@ #define TG3_LSO_RD_DMA_CRPTEN_CTRL 0x00004910 #define TG3_LSO_RD_DMA_CRPTEN_CTRL_BLEN_BD_4K 0x00030000 #define TG3_LSO_RD_DMA_CRPTEN_CTRL_BLEN_LSO_4K 0x000c0000 -#define TG3_LSO_RD_DMA_TX_LENGTH_WA 0x02000000 -/* 0x4914 --> 0x4be0 unused */ - -#define TG3_NUM_RDMA_CHANNELS 4 -#define TG3_RDMA_LENGTH 0x00004be0 +/* 0x4914 --> 0x4c00 unused */ /* Write DMA control registers */ #define WDMAC_MODE 0x00004c00 @@ -2963,7 +2959,6 @@ enum TG3_FLAGS { TG3_FLAG_L1PLLPD_EN, TG3_FLAG_APE_HAS_NCSI, TG3_FLAG_4K_FIFO_LIMIT, - TG3_FLAG_5719_RDMA_BUG, TG3_FLAG_RESET_TASK_PENDING, TG3_FLAG_5705_PLUS, TG3_FLAG_IS_5788, @@ -3112,7 +3107,6 @@ struct tg3 { int old_link; u8 phy_addr; - u8 phy_ape_lock; /* PHY info */ u32 phy_id; diff --git a/trunk/drivers/net/ethernet/chelsio/cxgb4/sge.c b/trunk/drivers/net/ethernet/chelsio/cxgb4/sge.c index d49933ed551f..8596acaa402b 100644 --- a/trunk/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/trunk/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -528,7 +528,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n, #endif while (n--) { - pg = __skb_alloc_page(gfp, NULL); + pg = alloc_page(gfp); if (unlikely(!pg)) { q->alloc_failed++; break; diff --git a/trunk/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/trunk/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index 8877fbfefb63..f2d1ecdcaf98 100644 --- a/trunk/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/trunk/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -653,7 +653,7 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl, alloc_small_pages: while (n--) { - page = __skb_alloc_page(gfp | __GFP_NOWARN, NULL); + page = alloc_page(gfp | __GFP_NOWARN | __GFP_COLD); if (unlikely(!page)) { fl->alloc_failed++; break; diff --git a/trunk/drivers/net/ethernet/intel/igb/igb_main.c b/trunk/drivers/net/ethernet/intel/igb/igb_main.c index b7c2d5050572..1050411e7ca3 100644 --- a/trunk/drivers/net/ethernet/intel/igb/igb_main.c +++ b/trunk/drivers/net/ethernet/intel/igb/igb_main.c @@ -6235,7 +6235,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, return true; if (!page) { - page = __skb_alloc_page(GFP_ATOMIC, bi->skb); + page = alloc_page(GFP_ATOMIC | __GFP_COLD); bi->page = page; if (unlikely(!page)) { rx_ring->rx_stats.alloc_failed++; diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 4326f74f7137..c709eae58c63 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1141,8 +1141,8 @@ static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring, /* alloc new page for storage */ if (likely(!page)) { - page = __skb_alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP, - bi->skb, ixgbe_rx_pg_order(rx_ring)); + page = alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP, + ixgbe_rx_pg_order(rx_ring)); if (unlikely(!page)) { rx_ring->rx_stats.alloc_rx_page_failed++; return false; diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 60ef64587412..3f9841d619ad 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -352,6 +352,7 @@ static void ixgbevf_alloc_rx_buffers(struct ixgbevf_adapter *adapter, adapter->alloc_rx_buff_failed++; goto no_buffers; } + bi->skb = skb; } if (!bi->dma) { diff --git a/trunk/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/trunk/drivers/net/ethernet/qlogic/qlge/qlge_main.c index b53a3b60b648..3769f5711cc3 100644 --- a/trunk/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/trunk/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4682,7 +4682,6 @@ static int __devinit qlge_probe(struct pci_dev *pdev, NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM; ndev->features = ndev->hw_features | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; - ndev->vlan_features = ndev->hw_features; if (test_bit(QL_DMA64, &qdev->flags)) ndev->features |= NETIF_F_HIGHDMA; diff --git a/trunk/drivers/net/ethernet/seeq/seeq8005.c b/trunk/drivers/net/ethernet/seeq/seeq8005.c index d6e50de71186..698edbbfc149 100644 --- a/trunk/drivers/net/ethernet/seeq/seeq8005.c +++ b/trunk/drivers/net/ethernet/seeq/seeq8005.c @@ -736,7 +736,9 @@ MODULE_PARM_DESC(irq, "SEEQ 8005 IRQ number"); int __init init_module(void) { dev_seeq = seeq8005_probe(-1); - return PTR_RET(dev_seeq); + if (IS_ERR(dev_seeq)) + return PTR_ERR(dev_seeq); + return 0; } void __exit cleanup_module(void) diff --git a/trunk/drivers/net/tun.c b/trunk/drivers/net/tun.c index 926d4db5cb38..c62163e272cd 100644 --- a/trunk/drivers/net/tun.c +++ b/trunk/drivers/net/tun.c @@ -1379,12 +1379,10 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, int vnet_hdr_sz; int ret; - if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) { + if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) if (copy_from_user(&ifr, argp, ifreq_len)) return -EFAULT; - } else { - memset(&ifr, 0, sizeof(ifr)); - } + if (cmd == TUNGETFEATURES) { /* Currently this just means: "what IFF flags are valid?". * This is needed because we never checked for invalid flags on diff --git a/trunk/drivers/net/usb/cdc-phonet.c b/trunk/drivers/net/usb/cdc-phonet.c index 64610048ce87..187c144c5e5b 100644 --- a/trunk/drivers/net/usb/cdc-phonet.c +++ b/trunk/drivers/net/usb/cdc-phonet.c @@ -130,7 +130,7 @@ static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) struct page *page; int err; - page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL); + page = alloc_page(gfp_flags); if (!page) return -ENOMEM; diff --git a/trunk/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/trunk/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 9ab24528f9b9..57bf1d7ee80f 100644 --- a/trunk/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/trunk/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -1188,7 +1188,7 @@ int brcmf_write_to_file(struct brcmf_pub *drvr, const u8 *buf, int size) kfree(buf); /* close file before return */ if (fp) - filp_close(fp, NULL); + filp_close(fp, current->files); /* restore previous address limit */ set_fs(old_fs); diff --git a/trunk/drivers/power/Kconfig b/trunk/drivers/power/Kconfig index c1892f321c46..aa764ecc4e60 100644 --- a/trunk/drivers/power/Kconfig +++ b/trunk/drivers/power/Kconfig @@ -268,7 +268,6 @@ config CHARGER_GPIO config CHARGER_MANAGER bool "Battery charger manager for multiple chargers" depends on REGULATOR && RTC_CLASS - select EXTCON help Say Y to enable charger-manager support, which allows multiple chargers attached to a battery and multiple batteries attached to a diff --git a/trunk/drivers/power/bq27x00_battery.c b/trunk/drivers/power/bq27x00_battery.c index 181ddece5181..f5d6d379f2fb 100644 --- a/trunk/drivers/power/bq27x00_battery.c +++ b/trunk/drivers/power/bq27x00_battery.c @@ -22,7 +22,6 @@ * Datasheets: * http://focus.ti.com/docs/prod/folders/print/bq27000.html * http://focus.ti.com/docs/prod/folders/print/bq27500.html - * http://www.ti.com/product/bq27425-g1 */ #include @@ -52,7 +51,6 @@ #define BQ27x00_REG_LMD 0x12 /* Last measured discharge */ #define BQ27x00_REG_CYCT 0x2A /* Cycle count total */ #define BQ27x00_REG_AE 0x22 /* Available energy */ -#define BQ27x00_POWER_AVG 0x24 #define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ #define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ @@ -68,21 +66,15 @@ #define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ #define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ #define BQ27500_FLAG_FC BIT(9) -#define BQ27500_FLAG_OTC BIT(15) - -/* bq27425 register addresses are same as bq27x00 addresses minus 4 */ -#define BQ27425_REG_OFFSET 0x04 -#define BQ27425_REG_SOC 0x18 /* Register address plus offset */ #define BQ27000_RS 20 /* Resistor sense */ -#define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000) struct bq27x00_device_info; struct bq27x00_access_methods { int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); }; -enum bq27x00_chip { BQ27000, BQ27500, BQ27425}; +enum bq27x00_chip { BQ27000, BQ27500 }; struct bq27x00_reg_cache { int temperature; @@ -94,8 +86,6 @@ struct bq27x00_reg_cache { int capacity; int energy; int flags; - int power_avg; - int health; }; struct bq27x00_device_info { @@ -133,22 +123,6 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_ENERGY_NOW, - POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_HEALTH, -}; - -static enum power_supply_property bq27425_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, }; static unsigned int poll_interval = 360; @@ -163,23 +137,9 @@ MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, bool single) { - if (di->chip == BQ27425) - return di->bus.read(di, reg - BQ27425_REG_OFFSET, single); return di->bus.read(di, reg, single); } -/* - * Higher versions of the chip like BQ27425 and BQ27500 - * differ from BQ27000 and BQ27200 in calculation of certain - * parameters. Hence we need to check for the chip type. - */ -static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di) -{ - if (di->chip == BQ27425 || di->chip == BQ27500) - return true; - return false; -} - /* * Return the battery Relative State-of-Charge * Or < 0 if something fails. @@ -190,8 +150,6 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) if (di->chip == BQ27500) rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); - else if (di->chip == BQ27425) - rsoc = bq27x00_read(di, BQ27425_REG_SOC, false); else rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true); @@ -216,7 +174,7 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg) return charge; } - if (bq27xxx_is_chip_version_higher(di)) + if (di->chip == BQ27500) charge *= 1000; else charge = charge * 3570 / BQ27000_RS; @@ -250,7 +208,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) { int ilmd; - if (bq27xxx_is_chip_version_higher(di)) + if (di->chip == BQ27500) ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); else ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); @@ -260,7 +218,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) return ilmd; } - if (bq27xxx_is_chip_version_higher(di)) + if (di->chip == BQ27500) ilmd *= 1000; else ilmd = ilmd * 256 * 3570 / BQ27000_RS; @@ -304,7 +262,7 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di) return temp; } - if (bq27xxx_is_chip_version_higher(di)) + if (di->chip == BQ27500) temp -= 2731; else temp = ((temp * 5) - 5463) / 2; @@ -348,70 +306,14 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg) return tval * 60; } -/* - * Read a power avg register. - * Return < 0 if something fails. - */ -static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg) -{ - int tval; - - tval = bq27x00_read(di, reg, false); - if (tval < 0) { - dev_err(di->dev, "error reading power avg rgister %02x: %d\n", - reg, tval); - return tval; - } - - if (di->chip == BQ27500) - return tval; - else - return (tval * BQ27x00_POWER_CONSTANT) / BQ27000_RS; -} - -/* - * Read flag register. - * Return < 0 if something fails. - */ -static int bq27x00_battery_read_health(struct bq27x00_device_info *di) -{ - int tval; - - tval = bq27x00_read(di, BQ27x00_REG_FLAGS, false); - if (tval < 0) { - dev_err(di->dev, "error reading flag register:%d\n", tval); - return tval; - } - - if ((di->chip == BQ27500)) { - if (tval & BQ27500_FLAG_SOCF) - tval = POWER_SUPPLY_HEALTH_DEAD; - else if (tval & BQ27500_FLAG_OTC) - tval = POWER_SUPPLY_HEALTH_OVERHEAT; - else - tval = POWER_SUPPLY_HEALTH_GOOD; - return tval; - } else { - if (tval & BQ27000_FLAG_EDV1) - tval = POWER_SUPPLY_HEALTH_DEAD; - else - tval = POWER_SUPPLY_HEALTH_GOOD; - return tval; - } - - return -1; -} - static void bq27x00_update(struct bq27x00_device_info *di) { struct bq27x00_reg_cache cache = {0, }; bool is_bq27500 = di->chip == BQ27500; - bool is_bq27425 = di->chip == BQ27425; cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); if (cache.flags >= 0) { - if (!is_bq27500 && !is_bq27425 - && (cache.flags & BQ27000_FLAG_CI)) { + if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) { dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n"); cache.capacity = -ENODATA; cache.energy = -ENODATA; @@ -419,30 +321,16 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.time_to_empty_avg = -ENODATA; cache.time_to_full = -ENODATA; cache.charge_full = -ENODATA; - cache.health = -ENODATA; } else { cache.capacity = bq27x00_battery_read_rsoc(di); - if (!is_bq27425) { - cache.energy = bq27x00_battery_read_energy(di); - cache.time_to_empty = - bq27x00_battery_read_time(di, - BQ27x00_REG_TTE); - cache.time_to_empty_avg = - bq27x00_battery_read_time(di, - BQ27x00_REG_TTECP); - cache.time_to_full = - bq27x00_battery_read_time(di, - BQ27x00_REG_TTF); - } + cache.energy = bq27x00_battery_read_energy(di); + cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE); + cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP); + cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF); cache.charge_full = bq27x00_battery_read_lmd(di); - cache.health = bq27x00_battery_read_health(di); } cache.temperature = bq27x00_battery_read_temperature(di); - if (!is_bq27425) - cache.cycle_count = bq27x00_battery_read_cyct(di); cache.cycle_count = bq27x00_battery_read_cyct(di); - cache.power_avg = - bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG); /* We only have to read charge design full once */ if (di->charge_design_full <= 0) @@ -488,7 +376,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, return curr; } - if (bq27xxx_is_chip_version_higher(di)) { + if (di->chip == BQ27500) { /* bq27500 returns signed value */ val->intval = (int)((s16)curr) * 1000; } else { @@ -509,7 +397,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, { int status; - if (bq27xxx_is_chip_version_higher(di)) { + if (di->chip == BQ27500) { if (di->cache.flags & BQ27500_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; else if (di->cache.flags & BQ27500_FLAG_DSC) @@ -537,7 +425,7 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di, { int level; - if (bq27xxx_is_chip_version_higher(di)) { + if (di->chip == BQ27500) { if (di->cache.flags & BQ27500_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; else if (di->cache.flags & BQ27500_FLAG_SOC1) @@ -662,12 +550,6 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ENERGY_NOW: ret = bq27x00_simple_value(di->cache.energy, val); break; - case POWER_SUPPLY_PROP_POWER_AVG: - ret = bq27x00_simple_value(di->cache.power_avg, val); - break; - case POWER_SUPPLY_PROP_HEALTH: - ret = bq27x00_simple_value(di->cache.health, val); - break; default: return -EINVAL; } @@ -688,14 +570,8 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) int ret; di->bat.type = POWER_SUPPLY_TYPE_BATTERY; - di->chip = BQ27425; - if (di->chip == BQ27425) { - di->bat.properties = bq27425_battery_props; - di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props); - } else { - di->bat.properties = bq27x00_battery_props; - di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); - } + di->bat.properties = bq27x00_battery_props; + di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); di->bat.get_property = bq27x00_battery_get_property; di->bat.external_power_changed = bq27x00_external_power_changed; @@ -853,7 +729,6 @@ static int bq27x00_battery_remove(struct i2c_client *client) static const struct i2c_device_id bq27x00_id[] = { { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */ { "bq27500", BQ27500 }, - { "bq27425", BQ27425 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27x00_id); diff --git a/trunk/drivers/power/charger-manager.c b/trunk/drivers/power/charger-manager.c index 526e5c931294..86935ec18954 100644 --- a/trunk/drivers/power/charger-manager.c +++ b/trunk/drivers/power/charger-manager.c @@ -271,13 +271,16 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) if (enable) { if (cm->emergency_stop) return -EAGAIN; - for (i = 0 ; i < desc->num_charger_regulators ; i++) - regulator_enable(desc->charger_regulators[i].consumer); + err = regulator_bulk_enable(desc->num_charger_regulators, + desc->charger_regulators); } else { /* * Abnormal battery state - Stop charging forcibly, * even if charger was enabled at the other places */ + err = regulator_bulk_disable(desc->num_charger_regulators, + desc->charger_regulators); + for (i = 0; i < desc->num_charger_regulators; i++) { if (regulator_is_enabled( desc->charger_regulators[i].consumer)) { @@ -285,7 +288,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) desc->charger_regulators[i].consumer); dev_warn(cm->dev, "Disable regulator(%s) forcibly.\n", - desc->charger_regulators[i].regulator_name); + desc->charger_regulators[i].supply); } } } @@ -991,92 +994,11 @@ int setup_charger_manager(struct charger_global_desc *gd) } EXPORT_SYMBOL_GPL(setup_charger_manager); -/** - * charger_extcon_work - enable/diable charger according to the state - * of charger cable - * - * @work: work_struct of the function charger_extcon_work. - */ -static void charger_extcon_work(struct work_struct *work) -{ - struct charger_cable *cable = - container_of(work, struct charger_cable, wq); - int ret; - - if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) { - ret = regulator_set_current_limit(cable->charger->consumer, - cable->min_uA, cable->max_uA); - if (ret < 0) { - pr_err("Cannot set current limit of %s (%s)\n", - cable->charger->regulator_name, cable->name); - return; - } - - pr_info("Set current limit of %s : %duA ~ %duA\n", - cable->charger->regulator_name, - cable->min_uA, cable->max_uA); - } - - try_charger_enable(cable->cm, cable->attached); -} - -/** - * charger_extcon_notifier - receive the state of charger cable - * when registered cable is attached or detached. - * - * @self: the notifier block of the charger_extcon_notifier. - * @event: the cable state. - * @ptr: the data pointer of notifier block. - */ -static int charger_extcon_notifier(struct notifier_block *self, - unsigned long event, void *ptr) -{ - struct charger_cable *cable = - container_of(self, struct charger_cable, nb); - - cable->attached = event; - schedule_work(&cable->wq); - - return NOTIFY_DONE; -} - -/** - * charger_extcon_init - register external connector to use it - * as the charger cable - * - * @cm: the Charger Manager representing the battery. - * @cable: the Charger cable representing the external connector. - */ -static int charger_extcon_init(struct charger_manager *cm, - struct charger_cable *cable) -{ - int ret = 0; - - /* - * Charger manager use Extcon framework to identify - * the charger cable among various external connector - * cable (e.g., TA, USB, MHL, Dock). - */ - INIT_WORK(&cable->wq, charger_extcon_work); - cable->nb.notifier_call = charger_extcon_notifier; - ret = extcon_register_interest(&cable->extcon_dev, - cable->extcon_name, cable->name, &cable->nb); - if (ret < 0) { - pr_info("Cannot register extcon_dev for %s(cable: %s).\n", - cable->extcon_name, - cable->name); - ret = -EINVAL; - } - - return ret; -} - static int charger_manager_probe(struct platform_device *pdev) { struct charger_desc *desc = dev_get_platdata(&pdev->dev); struct charger_manager *cm; int ret = 0, i = 0; - int j = 0; union power_supply_propval val; if (g_desc && !rtc_dev && g_desc->rtc_name) { @@ -1245,31 +1167,11 @@ static int charger_manager_probe(struct platform_device *pdev) goto err_register; } - for (i = 0 ; i < desc->num_charger_regulators ; i++) { - struct charger_regulator *charger - = &desc->charger_regulators[i]; - - charger->consumer = regulator_get(&pdev->dev, - charger->regulator_name); - if (charger->consumer == NULL) { - dev_err(&pdev->dev, "Cannot find charger(%s)n", - charger->regulator_name); - ret = -EINVAL; - goto err_chg_get; - } - - for (j = 0 ; j < charger->num_cables ; j++) { - struct charger_cable *cable = &charger->cables[j]; - - ret = charger_extcon_init(cm, cable); - if (ret < 0) { - dev_err(&pdev->dev, "Cannot find charger(%s)n", - charger->regulator_name); - goto err_extcon; - } - cable->charger = charger; - cable->cm = cm; - } + ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators, + desc->charger_regulators); + if (ret) { + dev_err(&pdev->dev, "Cannot get charger regulators.\n"); + goto err_bulk_get; } ret = try_charger_enable(cm, true); @@ -1295,19 +1197,9 @@ static int charger_manager_probe(struct platform_device *pdev) return 0; err_chg_enable: -err_extcon: - for (i = 0 ; i < desc->num_charger_regulators ; i++) { - struct charger_regulator *charger - = &desc->charger_regulators[i]; - for (j = 0 ; j < charger->num_cables ; j++) { - struct charger_cable *cable = &charger->cables[j]; - extcon_unregister_interest(&cable->extcon_dev); - } - } -err_chg_get: - for (i = 0 ; i < desc->num_charger_regulators ; i++) - regulator_put(desc->charger_regulators[i].consumer); - + regulator_bulk_free(desc->num_charger_regulators, + desc->charger_regulators); +err_bulk_get: power_supply_unregister(&cm->charger_psy); err_register: kfree(cm->charger_psy.properties); @@ -1326,8 +1218,6 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) { struct charger_manager *cm = platform_get_drvdata(pdev); struct charger_desc *desc = cm->desc; - int i = 0; - int j = 0; /* Remove from the list */ mutex_lock(&cm_list_mtx); @@ -1339,18 +1229,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) if (delayed_work_pending(&cm_monitor_work)) cancel_delayed_work_sync(&cm_monitor_work); - for (i = 0 ; i < desc->num_charger_regulators ; i++) { - struct charger_regulator *charger - = &desc->charger_regulators[i]; - for (j = 0 ; j < charger->num_cables ; j++) { - struct charger_cable *cable = &charger->cables[j]; - extcon_unregister_interest(&cable->extcon_dev); - } - } - - for (i = 0 ; i < desc->num_charger_regulators ; i++) - regulator_put(desc->charger_regulators[i].consumer); - + regulator_bulk_free(desc->num_charger_regulators, + desc->charger_regulators); power_supply_unregister(&cm->charger_psy); try_charger_enable(cm, false); diff --git a/trunk/drivers/power/ds2781_battery.c b/trunk/drivers/power/ds2781_battery.c index 7a1ff4e4cf9a..5f92a4bb33f9 100644 --- a/trunk/drivers/power/ds2781_battery.c +++ b/trunk/drivers/power/ds2781_battery.c @@ -64,7 +64,7 @@ static inline int ds2781_battery_io(struct ds2781_device_info *dev_info, return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io); } -static int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf, +int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf, int addr, size_t count) { return ds2781_battery_io(dev_info, buf, addr, count, 0); diff --git a/trunk/drivers/power/gpio-charger.c b/trunk/drivers/power/gpio-charger.c index cb2aa3195687..8672c9177dd7 100644 --- a/trunk/drivers/power/gpio-charger.c +++ b/trunk/drivers/power/gpio-charger.c @@ -54,7 +54,7 @@ static int gpio_charger_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - val->intval = gpio_get_value_cansleep(pdata->gpio); + val->intval = gpio_get_value(pdata->gpio); val->intval ^= pdata->gpio_active_low; break; default: diff --git a/trunk/drivers/power/lp8727_charger.c b/trunk/drivers/power/lp8727_charger.c index 6a364f4798f7..d8b75780bfef 100644 --- a/trunk/drivers/power/lp8727_charger.c +++ b/trunk/drivers/power/lp8727_charger.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #define DEBOUNCE_MSEC 270 diff --git a/trunk/drivers/power/max17042_battery.c b/trunk/drivers/power/max17042_battery.c index 74abc6c755b4..140788b309f8 100644 --- a/trunk/drivers/power/max17042_battery.c +++ b/trunk/drivers/power/max17042_battery.c @@ -113,7 +113,6 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, @@ -200,13 +199,6 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - val->intval = ret * 1000 / 2; - break; - case POWER_SUPPLY_PROP_CHARGE_COUNTER: - ret = max17042_read_reg(chip->client, MAX17042_QH); - if (ret < 0) - return ret; - val->intval = ret * 1000 / 2; break; case POWER_SUPPLY_PROP_TEMP: diff --git a/trunk/drivers/power/olpc_battery.c b/trunk/drivers/power/olpc_battery.c index 55b10b813353..7385092f9bc8 100644 --- a/trunk/drivers/power/olpc_battery.c +++ b/trunk/drivers/power/olpc_battery.c @@ -231,10 +231,12 @@ static int olpc_bat_get_charge_full_design(union power_supply_propval *val) case POWER_SUPPLY_TECHNOLOGY_LiFe: switch (mfr) { - case 1: /* Gold Peak, fall through */ - case 2: /* BYD */ + case 1: /* Gold Peak */ val->intval = 2800000; break; + case 2: /* BYD */ + val->intval = 3100000; + break; default: return -EIO; } @@ -265,55 +267,6 @@ static int olpc_bat_get_charge_now(union power_supply_propval *val) return 0; } -static int olpc_bat_get_voltage_max_design(union power_supply_propval *val) -{ - uint8_t ec_byte; - union power_supply_propval tech; - int mfr; - int ret; - - ret = olpc_bat_get_tech(&tech); - if (ret) - return ret; - - ec_byte = BAT_ADDR_MFR_TYPE; - ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); - if (ret) - return ret; - - mfr = ec_byte >> 4; - - switch (tech.intval) { - case POWER_SUPPLY_TECHNOLOGY_NiMH: - switch (mfr) { - case 1: /* Gold Peak */ - val->intval = 6000000; - break; - default: - return -EIO; - } - break; - - case POWER_SUPPLY_TECHNOLOGY_LiFe: - switch (mfr) { - case 1: /* Gold Peak */ - val->intval = 6400000; - break; - case 2: /* BYD */ - val->intval = 6500000; - break; - default: - return -EIO; - } - break; - - default: - return -EIO; - } - - return ret; -} - /********************************************************************* * Battery properties *********************************************************************/ @@ -448,11 +401,6 @@ static int olpc_bat_get_property(struct power_supply *psy, sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf)); val->strval = bat_serial; break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - ret = olpc_bat_get_voltage_max_design(val); - if (ret) - return ret; - break; default: ret = -EINVAL; break; @@ -480,7 +428,6 @@ static enum power_supply_property olpc_xo1_bat_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_CHARGE_COUNTER, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, }; /* XO-1.5 does not have ambient temperature property */ @@ -502,7 +449,6 @@ static enum power_supply_property olpc_xo15_bat_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_CHARGE_COUNTER, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, }; /* EEPROM reading goes completely around the power_supply API, sadly */ diff --git a/trunk/drivers/power/pda_power.c b/trunk/drivers/power/pda_power.c index 7312f2651647..8dbcd53c5e67 100644 --- a/trunk/drivers/power/pda_power.c +++ b/trunk/drivers/power/pda_power.c @@ -24,7 +24,11 @@ static inline unsigned int get_irq_flags(struct resource *res) { - return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK); + unsigned int flags = IRQF_SAMPLE_RANDOM | IRQF_SHARED; + + flags |= res->flags & IRQF_TRIGGER_MASK; + + return flags; } static struct device *dev; @@ -130,13 +134,13 @@ static void update_charger(void) regulator_set_current_limit(ac_draw, max_uA, max_uA); if (!regulator_enabled) { dev_dbg(dev, "charger on (AC)\n"); - WARN_ON(regulator_enable(ac_draw)); + regulator_enable(ac_draw); regulator_enabled = 1; } } else { if (regulator_enabled) { dev_dbg(dev, "charger off\n"); - WARN_ON(regulator_disable(ac_draw)); + regulator_disable(ac_draw); regulator_enabled = 0; } } diff --git a/trunk/drivers/power/power_supply_core.c b/trunk/drivers/power/power_supply_core.c index 08cc8a3c15af..6ad612726785 100644 --- a/trunk/drivers/power/power_supply_core.c +++ b/trunk/drivers/power/power_supply_core.c @@ -17,7 +17,6 @@ #include #include #include -#include #include "power_supply.h" /* exported for the APM Power driver, APM emulation */ @@ -170,63 +169,6 @@ static void power_supply_dev_release(struct device *dev) kfree(dev); } -#ifdef CONFIG_THERMAL -static int power_supply_read_temp(struct thermal_zone_device *tzd, - unsigned long *temp) -{ - struct power_supply *psy; - union power_supply_propval val; - int ret; - - WARN_ON(tzd == NULL); - psy = tzd->devdata; - ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); - - /* Convert tenths of degree Celsius to milli degree Celsius. */ - if (!ret) - *temp = val.intval * 100; - - return ret; -} - -static struct thermal_zone_device_ops psy_tzd_ops = { - .get_temp = power_supply_read_temp, -}; - -static int psy_register_thermal(struct power_supply *psy) -{ - int i; - - /* Register battery zone device psy reports temperature */ - for (i = 0; i < psy->num_properties; i++) { - if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) { - psy->tzd = thermal_zone_device_register(psy->name, 0, 0, - psy, &psy_tzd_ops, 0, 0, 0, 0); - if (IS_ERR(psy->tzd)) - return PTR_ERR(psy->tzd); - break; - } - } - return 0; -} - -static void psy_unregister_thermal(struct power_supply *psy) -{ - if (IS_ERR_OR_NULL(psy->tzd)) - return; - thermal_zone_device_unregister(psy->tzd); -} -#else -static int psy_register_thermal(struct power_supply *psy) -{ - return 0; -} - -static void psy_unregister_thermal(struct power_supply *psy) -{ -} -#endif - int power_supply_register(struct device *parent, struct power_supply *psy) { struct device *dev; @@ -255,10 +197,6 @@ int power_supply_register(struct device *parent, struct power_supply *psy) if (rc) goto device_add_failed; - rc = psy_register_thermal(psy); - if (rc) - goto register_thermal_failed; - rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; @@ -268,8 +206,6 @@ int power_supply_register(struct device *parent, struct power_supply *psy) goto success; create_triggers_failed: - psy_unregister_thermal(psy); -register_thermal_failed: device_del(dev); kobject_set_name_failed: device_add_failed: @@ -284,7 +220,6 @@ void power_supply_unregister(struct power_supply *psy) cancel_work_sync(&psy->changed_work); sysfs_remove_link(&psy->dev->kobj, "powers"); power_supply_remove_triggers(psy); - psy_unregister_thermal(psy); device_unregister(psy->dev); } EXPORT_SYMBOL_GPL(power_supply_unregister); diff --git a/trunk/drivers/power/power_supply_sysfs.c b/trunk/drivers/power/power_supply_sysfs.c index 1d96614a17a4..4150747f9186 100644 --- a/trunk/drivers/power/power_supply_sysfs.c +++ b/trunk/drivers/power/power_supply_sysfs.c @@ -159,8 +159,6 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charge_now), POWER_SUPPLY_ATTR(charge_avg), POWER_SUPPLY_ATTR(charge_counter), - POWER_SUPPLY_ATTR(constant_charge_current), - POWER_SUPPLY_ATTR(constant_charge_voltage), POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_full), @@ -168,15 +166,9 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(energy_now), POWER_SUPPLY_ATTR(energy_avg), POWER_SUPPLY_ATTR(capacity), - POWER_SUPPLY_ATTR(capacity_alert_min), - POWER_SUPPLY_ATTR(capacity_alert_max), POWER_SUPPLY_ATTR(capacity_level), POWER_SUPPLY_ATTR(temp), - POWER_SUPPLY_ATTR(temp_alert_min), - POWER_SUPPLY_ATTR(temp_alert_max), POWER_SUPPLY_ATTR(temp_ambient), - POWER_SUPPLY_ATTR(temp_ambient_alert_min), - POWER_SUPPLY_ATTR(temp_ambient_alert_max), POWER_SUPPLY_ATTR(time_to_empty_now), POWER_SUPPLY_ATTR(time_to_empty_avg), POWER_SUPPLY_ATTR(time_to_full_now), diff --git a/trunk/drivers/power/sbs-battery.c b/trunk/drivers/power/sbs-battery.c index a65e8f54157e..a5b6849d4123 100644 --- a/trunk/drivers/power/sbs-battery.c +++ b/trunk/drivers/power/sbs-battery.c @@ -469,7 +469,7 @@ static int sbs_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - goto done; /* don't trigger power_supply_changed()! */ + break; case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: diff --git a/trunk/drivers/power/smb347-charger.c b/trunk/drivers/power/smb347-charger.c index 332dd0110bda..f8eedd8a676f 100644 --- a/trunk/drivers/power/smb347-charger.c +++ b/trunk/drivers/power/smb347-charger.c @@ -196,14 +196,6 @@ static const unsigned int ccc_tbl[] = { 1200000, }; -/* Convert register value to current using lookup table */ -static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val) -{ - if (val >= size) - return -EINVAL; - return tbl[val]; -} - /* Convert current to register value using lookup table */ static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) { @@ -849,101 +841,22 @@ static int smb347_irq_init(struct smb347_charger *smb, return ret; } -/* - * Returns the constant charge current programmed - * into the charger in uA. - */ -static int get_const_charge_current(struct smb347_charger *smb) -{ - int ret, intval; - unsigned int v; - - if (!smb347_is_ps_online(smb)) - return -ENODATA; - - ret = regmap_read(smb->regmap, STAT_B, &v); - if (ret < 0) - return ret; - - /* - * The current value is composition of FCC and PCC values - * and we can detect which table to use from bit 5. - */ - if (v & 0x20) { - intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), v & 7); - } else { - v >>= 3; - intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), v & 7); - } - - return intval; -} - -/* - * Returns the constant charge voltage programmed - * into the charger in uV. - */ -static int get_const_charge_voltage(struct smb347_charger *smb) -{ - int ret, intval; - unsigned int v; - - if (!smb347_is_ps_online(smb)) - return -ENODATA; - - ret = regmap_read(smb->regmap, STAT_A, &v); - if (ret < 0) - return ret; - - v &= STAT_A_FLOAT_VOLTAGE_MASK; - if (v > 0x3d) - v = 0x3d; - - intval = 3500000 + v * 20000; - - return intval; -} - static int smb347_mains_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct smb347_charger *smb = container_of(psy, struct smb347_charger, mains); - int ret; - switch (prop) { - case POWER_SUPPLY_PROP_ONLINE: + if (prop == POWER_SUPPLY_PROP_ONLINE) { val->intval = smb->mains_online; - break; - - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: - ret = get_const_charge_voltage(smb); - if (ret < 0) - return ret; - else - val->intval = ret; - break; - - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - ret = get_const_charge_current(smb); - if (ret < 0) - return ret; - else - val->intval = ret; - break; - - default: - return -EINVAL; + return 0; } - - return 0; + return -EINVAL; } static enum power_supply_property smb347_mains_properties[] = { POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, }; static int smb347_usb_get_property(struct power_supply *psy, @@ -952,40 +865,16 @@ static int smb347_usb_get_property(struct power_supply *psy, { struct smb347_charger *smb = container_of(psy, struct smb347_charger, usb); - int ret; - switch (prop) { - case POWER_SUPPLY_PROP_ONLINE: + if (prop == POWER_SUPPLY_PROP_ONLINE) { val->intval = smb->usb_online; - break; - - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: - ret = get_const_charge_voltage(smb); - if (ret < 0) - return ret; - else - val->intval = ret; - break; - - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - ret = get_const_charge_current(smb); - if (ret < 0) - return ret; - else - val->intval = ret; - break; - - default: - return -EINVAL; + return 0; } - - return 0; + return -EINVAL; } static enum power_supply_property smb347_usb_properties[] = { POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, }; static int smb347_battery_get_property(struct power_supply *psy, diff --git a/trunk/drivers/power/test_power.c b/trunk/drivers/power/test_power.c index b99a452a4fda..b527c93bf2f3 100644 --- a/trunk/drivers/power/test_power.c +++ b/trunk/drivers/power/test_power.c @@ -22,13 +22,11 @@ #include static int ac_online = 1; -static int usb_online = 1; static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING; static int battery_health = POWER_SUPPLY_HEALTH_GOOD; static int battery_present = 1; /* true */ static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION; static int battery_capacity = 50; -static int battery_voltage = 3300; static int test_power_get_ac_property(struct power_supply *psy, enum power_supply_property psp, @@ -44,20 +42,6 @@ static int test_power_get_ac_property(struct power_supply *psy, return 0; } -static int test_power_get_usb_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - switch (psp) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = usb_online; - break; - default: - return -EINVAL; - } - return 0; -} - static int test_power_get_battery_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -102,12 +86,6 @@ static int test_power_get_battery_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: val->intval = 3600; break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = 26; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = battery_voltage; - break; default: pr_info("%s: some properties deliberately report errors.\n", __func__); @@ -136,8 +114,6 @@ static enum power_supply_property test_power_battery_props[] = { POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_VOLTAGE_NOW, }; static char *test_power_ac_supplied_to[] = { @@ -159,14 +135,6 @@ static struct power_supply test_power_supplies[] = { .properties = test_power_battery_props, .num_properties = ARRAY_SIZE(test_power_battery_props), .get_property = test_power_get_battery_property, - }, { - .name = "test_usb", - .type = POWER_SUPPLY_TYPE_USB, - .supplied_to = test_power_ac_supplied_to, - .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to), - .properties = test_power_ac_props, - .num_properties = ARRAY_SIZE(test_power_ac_props), - .get_property = test_power_get_usb_property, }, }; @@ -199,7 +167,6 @@ static void __exit test_power_exit(void) /* Let's see how we handle changes... */ ac_online = 0; - usb_online = 0; battery_status = POWER_SUPPLY_STATUS_DISCHARGING; for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) power_supply_changed(&test_power_supplies[i]); @@ -308,19 +275,6 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp) return strlen(buffer); } -static int param_set_usb_online(const char *key, const struct kernel_param *kp) -{ - usb_online = map_get_value(map_ac_online, key, usb_online); - power_supply_changed(&test_power_supplies[2]); - return 0; -} - -static int param_get_usb_online(char *buffer, const struct kernel_param *kp) -{ - strcpy(buffer, map_get_key(map_ac_online, usb_online, "unknown")); - return strlen(buffer); -} - static int param_set_battery_status(const char *key, const struct kernel_param *kp) { @@ -396,31 +350,13 @@ static int param_set_battery_capacity(const char *key, #define param_get_battery_capacity param_get_int -static int param_set_battery_voltage(const char *key, - const struct kernel_param *kp) -{ - int tmp; - - if (1 != sscanf(key, "%d", &tmp)) - return -EINVAL; - - battery_voltage = tmp; - power_supply_changed(&test_power_supplies[1]); - return 0; -} -#define param_get_battery_voltage param_get_int static struct kernel_param_ops param_ops_ac_online = { .set = param_set_ac_online, .get = param_get_ac_online, }; -static struct kernel_param_ops param_ops_usb_online = { - .set = param_set_usb_online, - .get = param_get_usb_online, -}; - static struct kernel_param_ops param_ops_battery_status = { .set = param_set_battery_status, .get = param_get_battery_status, @@ -446,27 +382,18 @@ static struct kernel_param_ops param_ops_battery_capacity = { .get = param_get_battery_capacity, }; -static struct kernel_param_ops param_ops_battery_voltage = { - .set = param_set_battery_voltage, - .get = param_get_battery_voltage, -}; #define param_check_ac_online(name, p) __param_check(name, p, void); -#define param_check_usb_online(name, p) __param_check(name, p, void); #define param_check_battery_status(name, p) __param_check(name, p, void); #define param_check_battery_present(name, p) __param_check(name, p, void); #define param_check_battery_technology(name, p) __param_check(name, p, void); #define param_check_battery_health(name, p) __param_check(name, p, void); #define param_check_battery_capacity(name, p) __param_check(name, p, void); -#define param_check_battery_voltage(name, p) __param_check(name, p, void); module_param(ac_online, ac_online, 0644); MODULE_PARM_DESC(ac_online, "AC charging state "); -module_param(usb_online, usb_online, 0644); -MODULE_PARM_DESC(usb_online, "USB charging state "); - module_param(battery_status, battery_status, 0644); MODULE_PARM_DESC(battery_status, "battery status "); @@ -486,8 +413,6 @@ MODULE_PARM_DESC(battery_health, module_param(battery_capacity, battery_capacity, 0644); MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)"); -module_param(battery_voltage, battery_voltage, 0644); -MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)"); MODULE_DESCRIPTION("Power supply driver for testing"); MODULE_AUTHOR("Anton Vorontsov "); diff --git a/trunk/drivers/power/twl4030_charger.c b/trunk/drivers/power/twl4030_charger.c index 15f4d5d8611b..7cacbaa68efe 100644 --- a/trunk/drivers/power/twl4030_charger.c +++ b/trunk/drivers/power/twl4030_charger.c @@ -22,7 +22,6 @@ #include #include #include -#include #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG 0x08 @@ -30,7 +29,6 @@ #define TWL4030_BCIVBUS 0x0c #define TWL4030_BCIMFSTS4 0x10 #define TWL4030_BCICTL1 0x23 -#define TWL4030_BB_CFG 0x12 #define TWL4030_BCIAUTOWEN BIT(5) #define TWL4030_CONFIG_DONE BIT(4) @@ -40,17 +38,6 @@ #define TWL4030_USBFASTMCHG BIT(2) #define TWL4030_STS_VBUS BIT(7) #define TWL4030_STS_USB_ID BIT(2) -#define TWL4030_BBCHEN BIT(4) -#define TWL4030_BBSEL_MASK 0b1100 -#define TWL4030_BBSEL_2V5 0b0000 -#define TWL4030_BBSEL_3V0 0b0100 -#define TWL4030_BBSEL_3V1 0b1000 -#define TWL4030_BBSEL_3V2 0b1100 -#define TWL4030_BBISEL_MASK 0b11 -#define TWL4030_BBISEL_25uA 0b00 -#define TWL4030_BBISEL_150uA 0b01 -#define TWL4030_BBISEL_500uA 0b10 -#define TWL4030_BBISEL_1000uA 0b11 /* BCI interrupts */ #define TWL4030_WOVF BIT(0) /* Watchdog overflow */ @@ -88,8 +75,6 @@ struct twl4030_bci { struct work_struct work; int irq_chg; int irq_bci; - struct regulator *usb_reg; - int usb_enabled; unsigned long event; }; @@ -119,7 +104,7 @@ static int twl4030_bci_read(u8 reg, u8 *val) static int twl4030_clear_set_boot_bci(u8 clear, u8 set) { - return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, clear, + return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0, TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set, TWL4030_PM_MASTER_BOOT_BCI); } @@ -167,14 +152,14 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci) } /* - * Enable/Disable USB Charge functionality. + * Enable/Disable USB Charge funtionality. */ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) { int ret; if (enable) { - /* Check for USB charger connected */ + /* Check for USB charger conneted */ if (!twl4030_bci_have_vbus(bci)) return -ENODEV; @@ -187,12 +172,6 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) return -EACCES; } - /* Need to keep regulator on */ - if (!bci->usb_enabled) { - regulator_enable(bci->usb_reg); - bci->usb_enabled = 1; - } - /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB); if (ret < 0) @@ -203,10 +182,6 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); } else { ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); - if (bci->usb_enabled) { - regulator_disable(bci->usb_reg); - bci->usb_enabled = 0; - } } return ret; @@ -227,49 +202,6 @@ static int twl4030_charger_enable_ac(bool enable) return ret; } -/* - * Enable/Disable charging of Backup Battery. - */ -static int twl4030_charger_enable_backup(int uvolt, int uamp) -{ - int ret; - u8 flags; - - if (uvolt < 2500000 || - uamp < 25) { - /* disable charging of backup battery */ - ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, - TWL4030_BBCHEN, 0, TWL4030_BB_CFG); - return ret; - } - - flags = TWL4030_BBCHEN; - if (uvolt >= 3200000) - flags |= TWL4030_BBSEL_3V2; - else if (uvolt >= 3100000) - flags |= TWL4030_BBSEL_3V1; - else if (uvolt >= 3000000) - flags |= TWL4030_BBSEL_3V0; - else - flags |= TWL4030_BBSEL_2V5; - - if (uamp >= 1000) - flags |= TWL4030_BBISEL_1000uA; - else if (uamp >= 500) - flags |= TWL4030_BBISEL_500uA; - else if (uamp >= 150) - flags |= TWL4030_BBISEL_150uA; - else - flags |= TWL4030_BBISEL_25uA; - - ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, - TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK, - flags, - TWL4030_BB_CFG); - - return ret; -} - /* * TWL4030 CHG_PRES (AC charger presence) events */ @@ -493,7 +425,6 @@ static enum power_supply_property twl4030_charger_props[] = { static int __init twl4030_bci_probe(struct platform_device *pdev) { struct twl4030_bci *bci; - struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; int ret; u32 reg; @@ -525,8 +456,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props); bci->usb.get_property = twl4030_bci_get_property; - bci->usb_reg = regulator_get(bci->dev, "bci3v1"); - ret = power_supply_register(&pdev->dev, &bci->usb); if (ret) { dev_err(&pdev->dev, "failed to register usb: %d\n", ret); @@ -575,8 +504,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) twl4030_charger_enable_ac(true); twl4030_charger_enable_usb(bci, true); - twl4030_charger_enable_backup(pdata->bb_uvolt, - pdata->bb_uamp); return 0; @@ -605,7 +532,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl4030_charger_enable_ac(false); twl4030_charger_enable_usb(bci, false); - twl4030_charger_enable_backup(0, 0); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, diff --git a/trunk/drivers/rtc/rtc-88pm80x.c b/trunk/drivers/rtc/rtc-88pm80x.c index 6367984e0565..a2f956d90de0 100644 --- a/trunk/drivers/rtc/rtc-88pm80x.c +++ b/trunk/drivers/rtc/rtc-88pm80x.c @@ -314,8 +314,8 @@ static int __devinit pm80x_rtc_probe(struct platform_device *pdev) info->rtc_dev = rtc_device_register("88pm80x-rtc", &pdev->dev, &pm80x_rtc_ops, THIS_MODULE); + ret = PTR_ERR(info->rtc_dev); if (IS_ERR(info->rtc_dev)) { - ret = PTR_ERR(info->rtc_dev); dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); goto out_rtc; } @@ -339,6 +339,7 @@ static int __devinit pm80x_rtc_probe(struct platform_device *pdev) out_rtc: pm80x_free_irq(chip, info->irq, info); out: + devm_kfree(&pdev->dev, info); return ret; } @@ -348,6 +349,7 @@ static int __devexit pm80x_rtc_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); rtc_device_unregister(info->rtc_dev); pm80x_free_irq(info->chip, info->irq, info); + devm_kfree(&pdev->dev, info); return 0; } diff --git a/trunk/drivers/rtc/rtc-wm831x.c b/trunk/drivers/rtc/rtc-wm831x.c index ea5c6f857ca5..59c6245e0421 100644 --- a/trunk/drivers/rtc/rtc-wm831x.c +++ b/trunk/drivers/rtc/rtc-wm831x.c @@ -24,7 +24,7 @@ #include #include #include -#include + /* * R16416 (0x4020) - RTC Write Counter @@ -96,26 +96,6 @@ struct wm831x_rtc { unsigned int alarm_enabled:1; }; -static void wm831x_rtc_add_randomness(struct wm831x *wm831x) -{ - int ret; - u16 reg; - - /* - * The write counter contains a pseudo-random number which is - * regenerated every time we set the RTC so it should be a - * useful per-system source of entropy. - */ - ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER); - if (ret >= 0) { - reg = ret; - add_device_randomness(®, sizeof(reg)); - } else { - dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n", - ret); - } -} - /* * Read current time and date in RTC */ @@ -451,8 +431,6 @@ static int wm831x_rtc_probe(struct platform_device *pdev) alm_irq, ret); } - wm831x_rtc_add_randomness(wm831x); - return 0; err: diff --git a/trunk/drivers/scsi/scsi_transport_fc.c b/trunk/drivers/scsi/scsi_transport_fc.c index e894ca7b54c0..2d1e68db9b3f 100644 --- a/trunk/drivers/scsi/scsi_transport_fc.c +++ b/trunk/drivers/scsi/scsi_transport_fc.c @@ -4146,7 +4146,45 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport) static void fc_bsg_remove(struct request_queue *q) { + struct request *req; /* block request */ + int counts; /* totals for request_list count and starved */ + if (q) { + /* Stop taking in new requests */ + spin_lock_irq(q->queue_lock); + blk_stop_queue(q); + + /* drain all requests in the queue */ + while (1) { + /* need the lock to fetch a request + * this may fetch the same reqeust as the previous pass + */ + req = blk_fetch_request(q); + /* save requests in use and starved */ + counts = q->rq.count[0] + q->rq.count[1] + + q->rq.starved[0] + q->rq.starved[1]; + spin_unlock_irq(q->queue_lock); + /* any requests still outstanding? */ + if (counts == 0) + break; + + /* This may be the same req as the previous iteration, + * always send the blk_end_request_all after a prefetch. + * It is not okay to not end the request because the + * prefetch started the request. + */ + if (req) { + /* return -ENXIO to indicate that this queue is + * going away + */ + req->errors = -ENXIO; + blk_end_request_all(req, -ENXIO); + } + + msleep(200); /* allow bsg to possibly finish */ + spin_lock_irq(q->queue_lock); + } + bsg_unregister_queue(q); blk_cleanup_queue(q); } diff --git a/trunk/drivers/scsi/scsi_transport_iscsi.c b/trunk/drivers/scsi/scsi_transport_iscsi.c index fa1dfaa83e32..09809d06eccb 100644 --- a/trunk/drivers/scsi/scsi_transport_iscsi.c +++ b/trunk/drivers/scsi/scsi_transport_iscsi.c @@ -575,7 +575,7 @@ static int iscsi_remove_host(struct transport_container *tc, struct iscsi_cls_host *ihost = shost->shost_data; if (ihost->bsg_q) { - bsg_unregister_queue(ihost->bsg_q); + bsg_remove_queue(ihost->bsg_q); blk_cleanup_queue(ihost->bsg_q); } return 0; diff --git a/trunk/drivers/spi/spi-omap2-mcspi.c b/trunk/drivers/spi/spi-omap2-mcspi.c index bc4778175e34..7d46b15e1520 100644 --- a/trunk/drivers/spi/spi-omap2-mcspi.c +++ b/trunk/drivers/spi/spi-omap2-mcspi.c @@ -28,8 +28,6 @@ #include #include #include -#include -#include #include #include #include @@ -41,6 +39,7 @@ #include +#include #include #include @@ -94,8 +93,8 @@ /* We have 2 DMA channels per CS, one for RX and one for TX */ struct omap2_mcspi_dma { - struct dma_chan *dma_tx; - struct dma_chan *dma_rx; + int dma_tx_channel; + int dma_rx_channel; int dma_tx_sync_dev; int dma_rx_sync_dev; @@ -301,46 +300,20 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) return 0; } -static void omap2_mcspi_rx_callback(void *data) -{ - struct spi_device *spi = data; - struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); - struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - - complete(&mcspi_dma->dma_rx_completion); - - /* We must disable the DMA RX request */ - omap2_mcspi_set_dma_req(spi, 1, 0); -} - -static void omap2_mcspi_tx_callback(void *data) -{ - struct spi_device *spi = data; - struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); - struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - - complete(&mcspi_dma->dma_tx_completion); - - /* We must disable the DMA TX request */ - omap2_mcspi_set_dma_req(spi, 0, 0); -} - static unsigned omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) { struct omap2_mcspi *mcspi; struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi_dma *mcspi_dma; - unsigned int count; - int word_len, element_count; + unsigned int count, c; + unsigned long base, tx_reg, rx_reg; + int word_len, data_type, element_count; int elements = 0; u32 l; u8 * rx; const u8 * tx; void __iomem *chstat_reg; - struct dma_slave_config cfg; - enum dma_slave_buswidth width; - unsigned es; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; @@ -348,92 +321,68 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; - if (cs->word_len <= 8) { - width = DMA_SLAVE_BUSWIDTH_1_BYTE; - es = 1; - } else if (cs->word_len <= 16) { - width = DMA_SLAVE_BUSWIDTH_2_BYTES; - es = 2; - } else { - width = DMA_SLAVE_BUSWIDTH_4_BYTES; - es = 4; - } - - memset(&cfg, 0, sizeof(cfg)); - cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; - cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; - cfg.src_addr_width = width; - cfg.dst_addr_width = width; - cfg.src_maxburst = 1; - cfg.dst_maxburst = 1; - - if (xfer->tx_buf && mcspi_dma->dma_tx) { - struct dma_async_tx_descriptor *tx; - struct scatterlist sg; - - dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); - - sg_init_table(&sg, 1); - sg_dma_address(&sg) = xfer->tx_dma; - sg_dma_len(&sg) = xfer->len; - - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1, - DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (tx) { - tx->callback = omap2_mcspi_tx_callback; - tx->callback_param = spi; - dmaengine_submit(tx); - } else { - /* FIXME: fall back to PIO? */ - } - } - - if (xfer->rx_buf && mcspi_dma->dma_rx) { - struct dma_async_tx_descriptor *tx; - struct scatterlist sg; - size_t len = xfer->len - es; - - dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); - - if (l & OMAP2_MCSPI_CHCONF_TURBO) - len -= es; - - sg_init_table(&sg, 1); - sg_dma_address(&sg) = xfer->rx_dma; - sg_dma_len(&sg) = len; - - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (tx) { - tx->callback = omap2_mcspi_rx_callback; - tx->callback_param = spi; - dmaengine_submit(tx); - } else { - /* FIXME: fall back to PIO? */ - } - } - count = xfer->len; + c = count; word_len = cs->word_len; + base = cs->phys; + tx_reg = base + OMAP2_MCSPI_TX0; + rx_reg = base + OMAP2_MCSPI_RX0; rx = xfer->rx_buf; tx = xfer->tx_buf; if (word_len <= 8) { + data_type = OMAP_DMA_DATA_TYPE_S8; element_count = count; } else if (word_len <= 16) { + data_type = OMAP_DMA_DATA_TYPE_S16; element_count = count >> 1; } else /* word_len <= 32 */ { + data_type = OMAP_DMA_DATA_TYPE_S32; element_count = count >> 2; } if (tx != NULL) { - dma_async_issue_pending(mcspi_dma->dma_tx); + omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel, + data_type, element_count, 1, + OMAP_DMA_SYNC_ELEMENT, + mcspi_dma->dma_tx_sync_dev, 0); + + omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + tx_reg, 0, 0); + + omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0, + OMAP_DMA_AMODE_POST_INC, + xfer->tx_dma, 0, 0); + } + + if (rx != NULL) { + elements = element_count - 1; + if (l & OMAP2_MCSPI_CHCONF_TURBO) + elements--; + + omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, + data_type, elements, 1, + OMAP_DMA_SYNC_ELEMENT, + mcspi_dma->dma_rx_sync_dev, 1); + + omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + rx_reg, 0, 0); + + omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0, + OMAP_DMA_AMODE_POST_INC, + xfer->rx_dma, 0, 0); + } + + if (tx != NULL) { + omap_start_dma(mcspi_dma->dma_tx_channel); omap2_mcspi_set_dma_req(spi, 0, 1); } if (rx != NULL) { - dma_async_issue_pending(mcspi_dma->dma_rx); + omap_start_dma(mcspi_dma->dma_rx_channel); omap2_mcspi_set_dma_req(spi, 1, 1); } @@ -459,10 +408,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) DMA_FROM_DEVICE); omap2_mcspi_set_enable(spi, 0); - elements = element_count - 1; - if (l & OMAP2_MCSPI_CHCONF_TURBO) { - elements--; if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) & OMAP2_MCSPI_CHSTAT_RXS)) { @@ -779,38 +725,64 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, return 0; } +static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); + + complete(&mcspi_dma->dma_rx_completion); + + /* We must disable the DMA RX request */ + omap2_mcspi_set_dma_req(spi, 1, 0); +} + +static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); + + complete(&mcspi_dma->dma_tx_completion); + + /* We must disable the DMA TX request */ + omap2_mcspi_set_dma_req(spi, 0, 0); +} + static int omap2_mcspi_request_dma(struct spi_device *spi) { struct spi_master *master = spi->master; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - dma_cap_mask_t mask; - unsigned sig; mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; - init_completion(&mcspi_dma->dma_rx_completion); - init_completion(&mcspi_dma->dma_tx_completion); - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - sig = mcspi_dma->dma_rx_sync_dev; - mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); - if (!mcspi_dma->dma_rx) { - dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n"); + if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX", + omap2_mcspi_dma_rx_callback, spi, + &mcspi_dma->dma_rx_channel)) { + dev_err(&spi->dev, "no RX DMA channel for McSPI\n"); return -EAGAIN; } - sig = mcspi_dma->dma_tx_sync_dev; - mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); - if (!mcspi_dma->dma_tx) { - dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n"); - dma_release_channel(mcspi_dma->dma_rx); - mcspi_dma->dma_rx = NULL; + if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX", + omap2_mcspi_dma_tx_callback, spi, + &mcspi_dma->dma_tx_channel)) { + omap_free_dma(mcspi_dma->dma_rx_channel); + mcspi_dma->dma_rx_channel = -1; + dev_err(&spi->dev, "no TX DMA channel for McSPI\n"); return -EAGAIN; } + init_completion(&mcspi_dma->dma_rx_completion); + init_completion(&mcspi_dma->dma_tx_completion); + return 0; } @@ -842,7 +814,8 @@ static int omap2_mcspi_setup(struct spi_device *spi) list_add_tail(&cs->node, &ctx->cs); } - if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) { + if (mcspi_dma->dma_rx_channel == -1 + || mcspi_dma->dma_tx_channel == -1) { ret = omap2_mcspi_request_dma(spi); if (ret < 0) return ret; @@ -877,13 +850,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) if (spi->chip_select < spi->master->num_chipselect) { mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - if (mcspi_dma->dma_rx) { - dma_release_channel(mcspi_dma->dma_rx); - mcspi_dma->dma_rx = NULL; + if (mcspi_dma->dma_rx_channel != -1) { + omap_free_dma(mcspi_dma->dma_rx_channel); + mcspi_dma->dma_rx_channel = -1; } - if (mcspi_dma->dma_tx) { - dma_release_channel(mcspi_dma->dma_tx); - mcspi_dma->dma_tx = NULL; + if (mcspi_dma->dma_tx_channel != -1) { + omap_free_dma(mcspi_dma->dma_tx_channel); + mcspi_dma->dma_tx_channel = -1; } } } @@ -1203,6 +1176,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) break; } + mcspi->dma_channels[i].dma_rx_channel = -1; mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start; sprintf(dma_ch_name, "tx%d", i); dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, @@ -1213,6 +1187,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) break; } + mcspi->dma_channels[i].dma_tx_channel = -1; mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start; } diff --git a/trunk/drivers/staging/bcm/Misc.c b/trunk/drivers/staging/bcm/Misc.c index f545716c666d..9a60d4cd2184 100644 --- a/trunk/drivers/staging/bcm/Misc.c +++ b/trunk/drivers/staging/bcm/Misc.c @@ -157,7 +157,12 @@ static int create_worker_threads(struct bcm_mini_adapter *psAdapter) static struct file *open_firmware_file(struct bcm_mini_adapter *Adapter, const char *path) { - struct file *flp = filp_open(path, O_RDONLY, S_IRWXU); + struct file *flp = NULL; + mm_segment_t oldfs; + oldfs = get_fs(); + set_fs(get_ds()); + flp = filp_open(path, O_RDONLY, S_IRWXU); + set_fs(oldfs); if (IS_ERR(flp)) { pr_err(DRV_NAME "Unable To Open File %s, err %ld", path, PTR_ERR(flp)); flp = NULL; @@ -178,12 +183,14 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u { int errorno = 0; struct file *flp = NULL; + mm_segment_t oldfs; struct timeval tv = {0}; flp = open_firmware_file(Adapter, path); if (!flp) { + errorno = -ENOENT; BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Unable to Open %s\n", path); - return -ENOENT; + goto exit_download; } BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Opened file is = %s and length =0x%lx to be downloaded at =0x%x", path, (unsigned long)flp->f_dentry->d_inode->i_size, loc); do_gettimeofday(&tv); @@ -194,7 +201,10 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u errorno = -EIO; goto exit_download; } + oldfs = get_fs(); + set_fs(get_ds()); vfs_llseek(flp, 0, 0); + set_fs(oldfs); if (Adapter->bcm_file_readback_from_chip(Adapter->pvInterfaceAdapter, flp, loc)) { BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Failed to read back firmware!"); errorno = -EIO; @@ -202,7 +212,12 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u } exit_download: - filp_close(flp, NULL); + oldfs = get_fs(); + set_fs(get_ds()); + if (flp && !(IS_ERR(flp))) + filp_close(flp, current->files); + set_fs(oldfs); + return errorno; } @@ -1041,8 +1056,10 @@ int InitCardAndDownloadFirmware(struct bcm_mini_adapter *ps_adapter) static int bcm_parse_target_params(struct bcm_mini_adapter *Adapter) { struct file *flp = NULL; + mm_segment_t oldfs = {0}; char *buff; int len = 0; + loff_t pos = 0; buff = kmalloc(BUFFER_1K, GFP_KERNEL); if (!buff) @@ -1062,16 +1079,20 @@ static int bcm_parse_target_params(struct bcm_mini_adapter *Adapter) Adapter->pstargetparams = NULL; return -ENOENT; } - len = kernel_read(flp, 0, buff, BUFFER_1K); - filp_close(flp, NULL); + oldfs = get_fs(); + set_fs(get_ds()); + len = vfs_read(flp, (void __user __force *)buff, BUFFER_1K, &pos); + set_fs(oldfs); if (len != sizeof(STARGETPARAMS)) { BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Mismatch in Target Param Structure!\n"); kfree(buff); kfree(Adapter->pstargetparams); Adapter->pstargetparams = NULL; + filp_close(flp, current->files); return -ENOENT; } + filp_close(flp, current->files); /* Check for autolink in config params */ /* diff --git a/trunk/drivers/staging/gdm72xx/sdio_boot.c b/trunk/drivers/staging/gdm72xx/sdio_boot.c index 65624bca8b3a..760efee23d4a 100644 --- a/trunk/drivers/staging/gdm72xx/sdio_boot.c +++ b/trunk/drivers/staging/gdm72xx/sdio_boot.c @@ -66,8 +66,9 @@ static int download_image(struct sdio_func *func, char *img_name) return -ENOENT; } - inode = filp->f_dentry->d_inode; - if (!S_ISREG(inode->i_mode)) { + if (filp->f_dentry) + inode = filp->f_dentry->d_inode; + if (!inode || !S_ISREG(inode->i_mode)) { printk(KERN_ERR "Invalid file type: %s\n", img_name); ret = -EINVAL; goto out; @@ -122,7 +123,7 @@ static int download_image(struct sdio_func *func, char *img_name) pno++; } out: - filp_close(filp, NULL); + filp_close(filp, current->files); return ret; } diff --git a/trunk/drivers/staging/gdm72xx/usb_boot.c b/trunk/drivers/staging/gdm72xx/usb_boot.c index e3dbd5a552ca..fef290c38db6 100644 --- a/trunk/drivers/staging/gdm72xx/usb_boot.c +++ b/trunk/drivers/staging/gdm72xx/usb_boot.c @@ -173,12 +173,14 @@ int usb_boot(struct usb_device *usbdev, u16 pid) filp = filp_open(img_name, O_RDONLY | O_LARGEFILE, 0); if (IS_ERR(filp)) { printk(KERN_ERR "Can't find %s.\n", img_name); + set_fs(fs); ret = PTR_ERR(filp); goto restore_fs; } - inode = filp->f_dentry->d_inode; - if (!S_ISREG(inode->i_mode)) { + if (filp->f_dentry) + inode = filp->f_dentry->d_inode; + if (!inode || !S_ISREG(inode->i_mode)) { printk(KERN_ERR "Invalid file type: %s\n", img_name); ret = -EINVAL; goto out; @@ -260,7 +262,7 @@ int usb_boot(struct usb_device *usbdev, u16 pid) ret = -EINVAL; } out: - filp_close(filp, NULL); + filp_close(filp, current->files); restore_fs: set_fs(fs); @@ -320,11 +322,13 @@ static int em_download_image(struct usb_device *usbdev, char *path, goto restore_fs; } - inode = filp->f_dentry->d_inode; - if (!S_ISREG(inode->i_mode)) { - printk(KERN_ERR "Invalid file type: %s\n", path); - ret = -EINVAL; - goto out; + if (filp->f_dentry) { + inode = filp->f_dentry->d_inode; + if (!inode || !S_ISREG(inode->i_mode)) { + printk(KERN_ERR "Invalid file type: %s\n", path); + ret = -EINVAL; + goto out; + } } buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL); @@ -360,7 +364,7 @@ static int em_download_image(struct usb_device *usbdev, char *path, goto out; out: - filp_close(filp, NULL); + filp_close(filp, current->files); restore_fs: set_fs(fs); diff --git a/trunk/drivers/staging/media/dt3155v4l/dt3155v4l.c b/trunk/drivers/staging/media/dt3155v4l/dt3155v4l.c index ebe5a27c06f5..c365cdf714ea 100644 --- a/trunk/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/trunk/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -971,7 +971,20 @@ static struct pci_driver pci_driver = { .remove = __devexit_p(dt3155_remove), }; -module_pci_driver(pci_driver); +static int __init +dt3155_init_module(void) +{ + return pci_register_driver(&pci_driver); +} + +static void __exit +dt3155_exit_module(void) +{ + pci_unregister_driver(&pci_driver); +} + +module_init(dt3155_init_module); +module_exit(dt3155_exit_module); MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber"); MODULE_AUTHOR("Marin Mitov "); diff --git a/trunk/drivers/staging/media/easycap/easycap_main.c b/trunk/drivers/staging/media/easycap/easycap_main.c index 8269c77dbf7d..a1c45e4dcdce 100644 --- a/trunk/drivers/staging/media/easycap/easycap_main.c +++ b/trunk/drivers/staging/media/easycap/easycap_main.c @@ -3083,7 +3083,6 @@ static int create_video_urbs(struct easycap *peasycap) peasycap->allocation_video_urb += 1; pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); if (!pdata_urb) { - usb_free_urb(purb); SAM("ERROR: Could not allocate struct data_urb.\n"); return -ENOMEM; } diff --git a/trunk/drivers/staging/media/lirc/lirc_bt829.c b/trunk/drivers/staging/media/lirc/lirc_bt829.c index 951007a3fc96..4d20e9f74118 100644 --- a/trunk/drivers/staging/media/lirc/lirc_bt829.c +++ b/trunk/drivers/staging/media/lirc/lirc_bt829.c @@ -171,7 +171,7 @@ static void cycle_delay(int cycle) } -static int poll_main(void) +static int poll_main() { unsigned char status_high, status_low; diff --git a/trunk/drivers/staging/media/solo6x10/core.c b/trunk/drivers/staging/media/solo6x10/core.c index 3ee9b125797f..d2fd842e37cf 100644 --- a/trunk/drivers/staging/media/solo6x10/core.c +++ b/trunk/drivers/staging/media/solo6x10/core.c @@ -318,4 +318,15 @@ static struct pci_driver solo_pci_driver = { .remove = solo_pci_remove, }; -module_pci_driver(solo_pci_driver); +static int __init solo_module_init(void) +{ + return pci_register_driver(&solo_pci_driver); +} + +static void __exit solo_module_exit(void) +{ + pci_unregister_driver(&solo_pci_driver); +} + +module_init(solo_module_init); +module_exit(solo_module_exit); diff --git a/trunk/drivers/target/target_core_file.c b/trunk/drivers/target/target_core_file.c index cbb5aaf3e567..9e2100551c78 100644 --- a/trunk/drivers/target/target_core_file.c +++ b/trunk/drivers/target/target_core_file.c @@ -109,29 +109,46 @@ static struct se_device *fd_create_virtdevice( struct se_subsystem_dev *se_dev, void *p) { + char *dev_p = NULL; struct se_device *dev; struct se_dev_limits dev_limits; struct queue_limits *limits; struct fd_dev *fd_dev = p; struct fd_host *fd_host = hba->hba_ptr; + mm_segment_t old_fs; struct file *file; struct inode *inode = NULL; int dev_flags = 0, flags, ret = -EINVAL; memset(&dev_limits, 0, sizeof(struct se_dev_limits)); + old_fs = get_fs(); + set_fs(get_ds()); + dev_p = getname(fd_dev->fd_dev_name); + set_fs(old_fs); + + if (IS_ERR(dev_p)) { + pr_err("getname(%s) failed: %lu\n", + fd_dev->fd_dev_name, IS_ERR(dev_p)); + ret = PTR_ERR(dev_p); + goto fail; + } /* * Use O_DSYNC by default instead of O_SYNC to forgo syncing * of pure timestamp updates. */ flags = O_RDWR | O_CREAT | O_LARGEFILE | O_DSYNC; - file = filp_open(fd_dev->fd_dev_name, flags, 0600); + file = filp_open(dev_p, flags, 0600); if (IS_ERR(file)) { - pr_err("filp_open(%s) failed\n", fd_dev->fd_dev_name); + pr_err("filp_open(%s) failed\n", dev_p); ret = PTR_ERR(file); goto fail; } + if (!file || !file->f_dentry) { + pr_err("filp_open(%s) failed\n", dev_p); + goto fail; + } fd_dev->fd_file = file; /* * If using a block backend with this struct file, we extract @@ -195,12 +212,14 @@ static struct se_device *fd_create_virtdevice( " %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id, fd_dev->fd_dev_name, fd_dev->fd_dev_size); + putname(dev_p); return dev; fail: if (fd_dev->fd_file) { filp_close(fd_dev->fd_file, NULL); fd_dev->fd_file = NULL; } + putname(dev_p); return ERR_PTR(ret); } @@ -433,11 +452,14 @@ static ssize_t fd_set_configfs_dev_params( token = match_token(ptr, tokens, args); switch (token) { case Opt_fd_dev_name: - if (match_strlcpy(fd_dev->fd_dev_name, &args[0], - FD_MAX_DEV_NAME) == 0) { - ret = -EINVAL; + arg_p = match_strdup(&args[0]); + if (!arg_p) { + ret = -ENOMEM; break; } + snprintf(fd_dev->fd_dev_name, FD_MAX_DEV_NAME, + "%s", arg_p); + kfree(arg_p); pr_debug("FILEIO: Referencing Path: %s\n", fd_dev->fd_dev_name); fd_dev->fbd_flags |= FBDF_HAS_PATH; diff --git a/trunk/drivers/thermal/thermal_sys.c b/trunk/drivers/thermal/thermal_sys.c index 2ab31e4f02cc..2d7a9fe8f365 100644 --- a/trunk/drivers/thermal/thermal_sys.c +++ b/trunk/drivers/thermal/thermal_sys.c @@ -1251,7 +1251,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * longer needed. The passive cooling formula uses tc1 and tc2 as described in * section 11.1.5.1 of the ACPI specification 3.0. */ -struct thermal_zone_device *thermal_zone_device_register(const char *type, +struct thermal_zone_device *thermal_zone_device_register(char *type, int trips, int mask, void *devdata, const struct thermal_zone_device_ops *ops, int tc1, int tc2, int passive_delay, int polling_delay) diff --git a/trunk/drivers/tty/serial/uartlite.c b/trunk/drivers/tty/serial/uartlite.c index 6579ffdd8e9b..6cd414341d5e 100644 --- a/trunk/drivers/tty/serial/uartlite.c +++ b/trunk/drivers/tty/serial/uartlite.c @@ -216,7 +216,8 @@ static int ulite_startup(struct uart_port *port) { int ret; - ret = request_irq(port->irq, ulite_isr, IRQF_SHARED, "uartlite", port); + ret = request_irq(port->irq, ulite_isr, + IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port); if (ret) return ret; diff --git a/trunk/drivers/usb/core/hub.c b/trunk/drivers/usb/core/hub.c index 128a804c42f4..821126eb8176 100644 --- a/trunk/drivers/usb/core/hub.c +++ b/trunk/drivers/usb/core/hub.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -2182,14 +2181,6 @@ int usb_new_device(struct usb_device *udev) /* Tell the world! */ announce_device(udev); - if (udev->serial) - add_device_randomness(udev->serial, strlen(udev->serial)); - if (udev->product) - add_device_randomness(udev->product, strlen(udev->product)); - if (udev->manufacturer) - add_device_randomness(udev->manufacturer, - strlen(udev->manufacturer)); - device_enable_async_suspend(&udev->dev); /* diff --git a/trunk/drivers/usb/gadget/f_phonet.c b/trunk/drivers/usb/gadget/f_phonet.c index 8ee9268fe253..965a6293206a 100644 --- a/trunk/drivers/usb/gadget/f_phonet.c +++ b/trunk/drivers/usb/gadget/f_phonet.c @@ -301,7 +301,7 @@ pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags) struct page *page; int err; - page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL); + page = alloc_page(gfp_flags); if (!page) return -ENOMEM; diff --git a/trunk/drivers/usb/gadget/goku_udc.c b/trunk/drivers/usb/gadget/goku_udc.c index 9fd7886cfa9a..3d28fb976c78 100644 --- a/trunk/drivers/usb/gadget/goku_udc.c +++ b/trunk/drivers/usb/gadget/goku_udc.c @@ -1836,7 +1836,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* init to known state, then setup irqs */ udc_reset(dev); udc_reinit (dev); - if (request_irq(pdev->irq, goku_irq, IRQF_SHARED, + if (request_irq(pdev->irq, goku_irq, IRQF_SHARED/*|IRQF_SAMPLE_RANDOM*/, driver_name, dev) != 0) { DBG(dev, "request interrupt %d failed\n", pdev->irq); retval = -EBUSY; diff --git a/trunk/drivers/usb/gadget/pxa25x_udc.c b/trunk/drivers/usb/gadget/pxa25x_udc.c index 907ad3ecb341..53c093b941e5 100644 --- a/trunk/drivers/usb/gadget/pxa25x_udc.c +++ b/trunk/drivers/usb/gadget/pxa25x_udc.c @@ -2201,15 +2201,19 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) #ifdef CONFIG_ARCH_LUBBOCK if (machine_is_lubbock()) { - retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, - 0, driver_name, dev); + retval = request_irq(LUBBOCK_USB_DISC_IRQ, + lubbock_vbus_irq, + IRQF_SAMPLE_RANDOM, + driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval); goto err_irq_lub; } - retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, - 0, driver_name, dev); + retval = request_irq(LUBBOCK_USB_IRQ, + lubbock_vbus_irq, + IRQF_SAMPLE_RANDOM, + driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_IRQ, retval); diff --git a/trunk/drivers/usb/gadget/storage_common.c b/trunk/drivers/usb/gadget/storage_common.c index 8d9bcd8207c8..ae8b18869b8c 100644 --- a/trunk/drivers/usb/gadget/storage_common.c +++ b/trunk/drivers/usb/gadget/storage_common.c @@ -656,8 +656,9 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) if (!(filp->f_mode & FMODE_WRITE)) ro = 1; - inode = filp->f_path.dentry->d_inode; - if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) { + if (filp->f_path.dentry) + inode = filp->f_path.dentry->d_inode; + if (!inode || (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) { LINFO(curlun, "invalid file type: %s\n", filename); goto out; } @@ -666,7 +667,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) * If we can't read the file, it's no good. * If we can't write the file, use it read-only. */ - if (!(filp->f_op->read || filp->f_op->aio_read)) { + if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { LINFO(curlun, "file not readable: %s\n", filename); goto out; } @@ -711,6 +712,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) if (fsg_lun_is_open(curlun)) fsg_lun_close(curlun); + get_file(filp); curlun->blksize = blksize; curlun->blkbits = blkbits; curlun->ro = ro; @@ -718,10 +720,10 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) curlun->file_length = size; curlun->num_sectors = num_sectors; LDBG(curlun, "open backing file: %s\n", filename); - return 0; + rc = 0; out: - fput(filp); + filp_close(filp, current->files); return rc; } diff --git a/trunk/drivers/usb/gadget/u_uac1.c b/trunk/drivers/usb/gadget/u_uac1.c index e0c5e88e03ed..af9898982059 100644 --- a/trunk/drivers/usb/gadget/u_uac1.c +++ b/trunk/drivers/usb/gadget/u_uac1.c @@ -275,17 +275,17 @@ static int gaudio_close_snd_dev(struct gaudio *gau) /* Close control device */ snd = &gau->control; if (snd->filp) - filp_close(snd->filp, NULL); + filp_close(snd->filp, current->files); /* Close PCM playback device and setup substream */ snd = &gau->playback; if (snd->filp) - filp_close(snd->filp, NULL); + filp_close(snd->filp, current->files); /* Close PCM capture device and setup substream */ snd = &gau->capture; if (snd->filp) - filp_close(snd->filp, NULL); + filp_close(snd->filp, current->files); return 0; } diff --git a/trunk/drivers/usb/otg/isp1301_omap.c b/trunk/drivers/usb/otg/isp1301_omap.c index 7a88667742b6..575fc815c932 100644 --- a/trunk/drivers/usb/otg/isp1301_omap.c +++ b/trunk/drivers/usb/otg/isp1301_omap.c @@ -1576,6 +1576,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) isp->irq_type = IRQF_TRIGGER_FALLING; } + isp->irq_type |= IRQF_SAMPLE_RANDOM; status = request_irq(i2c->irq, isp1301_irq, isp->irq_type, DRIVER_NAME, isp); if (status < 0) { diff --git a/trunk/drivers/vfio/Kconfig b/trunk/drivers/vfio/Kconfig deleted file mode 100644 index 7cd5dec0abd1..000000000000 --- a/trunk/drivers/vfio/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -config VFIO_IOMMU_TYPE1 - tristate - depends on VFIO - default n - -menuconfig VFIO - tristate "VFIO Non-Privileged userspace driver framework" - depends on IOMMU_API - select VFIO_IOMMU_TYPE1 if X86 - help - VFIO provides a framework for secure userspace device drivers. - See Documentation/vfio.txt for more details. - - If you don't know what to do here, say N. - -source "drivers/vfio/pci/Kconfig" diff --git a/trunk/drivers/vfio/Makefile b/trunk/drivers/vfio/Makefile deleted file mode 100644 index 2398d4a0e38b..000000000000 --- a/trunk/drivers/vfio/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_VFIO) += vfio.o -obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o -obj-$(CONFIG_VFIO_PCI) += pci/ diff --git a/trunk/drivers/vfio/pci/Kconfig b/trunk/drivers/vfio/pci/Kconfig deleted file mode 100644 index 5980758563eb..000000000000 --- a/trunk/drivers/vfio/pci/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config VFIO_PCI - tristate "VFIO support for PCI devices" - depends on VFIO && PCI && EVENTFD - help - Support for the PCI VFIO bus driver. This is required to make - use of PCI drivers using the VFIO framework. - - If you don't know what to do here, say N. diff --git a/trunk/drivers/vfio/pci/Makefile b/trunk/drivers/vfio/pci/Makefile deleted file mode 100644 index 131079255fd9..000000000000 --- a/trunk/drivers/vfio/pci/Makefile +++ /dev/null @@ -1,4 +0,0 @@ - -vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o - -obj-$(CONFIG_VFIO_PCI) += vfio-pci.o diff --git a/trunk/drivers/vfio/pci/vfio_pci.c b/trunk/drivers/vfio/pci/vfio_pci.c deleted file mode 100644 index 6968b7232232..000000000000 --- a/trunk/drivers/vfio/pci/vfio_pci.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * Author: Alex Williamson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from original vfio: - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * Author: Tom Lyon, pugs@cisco.com - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vfio_pci_private.h" - -#define DRIVER_VERSION "0.2" -#define DRIVER_AUTHOR "Alex Williamson " -#define DRIVER_DESC "VFIO PCI - User Level meta-driver" - -static bool nointxmask; -module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(nointxmask, - "Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag."); - -static int vfio_pci_enable(struct vfio_pci_device *vdev) -{ - struct pci_dev *pdev = vdev->pdev; - int ret; - u16 cmd; - u8 msix_pos; - - vdev->reset_works = (pci_reset_function(pdev) == 0); - pci_save_state(pdev); - vdev->pci_saved_state = pci_store_saved_state(pdev); - if (!vdev->pci_saved_state) - pr_debug("%s: Couldn't store %s saved state\n", - __func__, dev_name(&pdev->dev)); - - ret = vfio_config_init(vdev); - if (ret) - goto out; - - if (likely(!nointxmask)) - vdev->pci_2_3 = pci_intx_mask_supported(pdev); - - pci_read_config_word(pdev, PCI_COMMAND, &cmd); - if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) { - cmd &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(pdev, PCI_COMMAND, cmd); - } - - msix_pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); - if (msix_pos) { - u16 flags; - u32 table; - - pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags); - pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table); - - vdev->msix_bar = table & PCI_MSIX_FLAGS_BIRMASK; - vdev->msix_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; - vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16; - } else - vdev->msix_bar = 0xFF; - - ret = pci_enable_device(pdev); - if (ret) - goto out; - - return ret; - -out: - kfree(vdev->pci_saved_state); - vdev->pci_saved_state = NULL; - vfio_config_free(vdev); - return ret; -} - -static void vfio_pci_disable(struct vfio_pci_device *vdev) -{ - int bar; - - pci_disable_device(vdev->pdev); - - vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE | - VFIO_IRQ_SET_ACTION_TRIGGER, - vdev->irq_type, 0, 0, NULL); - - vdev->virq_disabled = false; - - vfio_config_free(vdev); - - pci_reset_function(vdev->pdev); - - if (pci_load_and_free_saved_state(vdev->pdev, - &vdev->pci_saved_state) == 0) - pci_restore_state(vdev->pdev); - else - pr_info("%s: Couldn't reload %s saved state\n", - __func__, dev_name(&vdev->pdev->dev)); - - for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) { - if (!vdev->barmap[bar]) - continue; - pci_iounmap(vdev->pdev, vdev->barmap[bar]); - pci_release_selected_regions(vdev->pdev, 1 << bar); - vdev->barmap[bar] = NULL; - } -} - -static void vfio_pci_release(void *device_data) -{ - struct vfio_pci_device *vdev = device_data; - - if (atomic_dec_and_test(&vdev->refcnt)) - vfio_pci_disable(vdev); - - module_put(THIS_MODULE); -} - -static int vfio_pci_open(void *device_data) -{ - struct vfio_pci_device *vdev = device_data; - - if (!try_module_get(THIS_MODULE)) - return -ENODEV; - - if (atomic_inc_return(&vdev->refcnt) == 1) { - int ret = vfio_pci_enable(vdev); - if (ret) { - module_put(THIS_MODULE); - return ret; - } - } - - return 0; -} - -static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) -{ - if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) { - u8 pin; - pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin); - if (pin) - return 1; - - } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) { - u8 pos; - u16 flags; - - pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSI); - if (pos) { - pci_read_config_word(vdev->pdev, - pos + PCI_MSI_FLAGS, &flags); - - return 1 << (flags & PCI_MSI_FLAGS_QMASK); - } - } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) { - u8 pos; - u16 flags; - - pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSIX); - if (pos) { - pci_read_config_word(vdev->pdev, - pos + PCI_MSIX_FLAGS, &flags); - - return (flags & PCI_MSIX_FLAGS_QSIZE) + 1; - } - } - - return 0; -} - -static long vfio_pci_ioctl(void *device_data, - unsigned int cmd, unsigned long arg) -{ - struct vfio_pci_device *vdev = device_data; - unsigned long minsz; - - if (cmd == VFIO_DEVICE_GET_INFO) { - struct vfio_device_info info; - - minsz = offsetofend(struct vfio_device_info, num_irqs); - - if (copy_from_user(&info, (void __user *)arg, minsz)) - return -EFAULT; - - if (info.argsz < minsz) - return -EINVAL; - - info.flags = VFIO_DEVICE_FLAGS_PCI; - - if (vdev->reset_works) - info.flags |= VFIO_DEVICE_FLAGS_RESET; - - info.num_regions = VFIO_PCI_NUM_REGIONS; - info.num_irqs = VFIO_PCI_NUM_IRQS; - - return copy_to_user((void __user *)arg, &info, minsz); - - } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) { - struct pci_dev *pdev = vdev->pdev; - struct vfio_region_info info; - - minsz = offsetofend(struct vfio_region_info, offset); - - if (copy_from_user(&info, (void __user *)arg, minsz)) - return -EFAULT; - - if (info.argsz < minsz) - return -EINVAL; - - switch (info.index) { - case VFIO_PCI_CONFIG_REGION_INDEX: - info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); - info.size = pdev->cfg_size; - info.flags = VFIO_REGION_INFO_FLAG_READ | - VFIO_REGION_INFO_FLAG_WRITE; - break; - case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: - info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); - info.size = pci_resource_len(pdev, info.index); - if (!info.size) { - info.flags = 0; - break; - } - - info.flags = VFIO_REGION_INFO_FLAG_READ | - VFIO_REGION_INFO_FLAG_WRITE; - if (pci_resource_flags(pdev, info.index) & - IORESOURCE_MEM && info.size >= PAGE_SIZE) - info.flags |= VFIO_REGION_INFO_FLAG_MMAP; - break; - case VFIO_PCI_ROM_REGION_INDEX: - { - void __iomem *io; - size_t size; - - info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); - info.flags = 0; - - /* Report the BAR size, not the ROM size */ - info.size = pci_resource_len(pdev, info.index); - if (!info.size) - break; - - /* Is it really there? */ - io = pci_map_rom(pdev, &size); - if (!io || !size) { - info.size = 0; - break; - } - pci_unmap_rom(pdev, io); - - info.flags = VFIO_REGION_INFO_FLAG_READ; - break; - } - default: - return -EINVAL; - } - - return copy_to_user((void __user *)arg, &info, minsz); - - } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) { - struct vfio_irq_info info; - - minsz = offsetofend(struct vfio_irq_info, count); - - if (copy_from_user(&info, (void __user *)arg, minsz)) - return -EFAULT; - - if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS) - return -EINVAL; - - info.flags = VFIO_IRQ_INFO_EVENTFD; - - info.count = vfio_pci_get_irq_count(vdev, info.index); - - if (info.index == VFIO_PCI_INTX_IRQ_INDEX) - info.flags |= (VFIO_IRQ_INFO_MASKABLE | - VFIO_IRQ_INFO_AUTOMASKED); - else - info.flags |= VFIO_IRQ_INFO_NORESIZE; - - return copy_to_user((void __user *)arg, &info, minsz); - - } else if (cmd == VFIO_DEVICE_SET_IRQS) { - struct vfio_irq_set hdr; - u8 *data = NULL; - int ret = 0; - - minsz = offsetofend(struct vfio_irq_set, count); - - if (copy_from_user(&hdr, (void __user *)arg, minsz)) - return -EFAULT; - - if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS || - hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | - VFIO_IRQ_SET_ACTION_TYPE_MASK)) - return -EINVAL; - - if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { - size_t size; - - if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) - size = sizeof(uint8_t); - else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) - size = sizeof(int32_t); - else - return -EINVAL; - - if (hdr.argsz - minsz < hdr.count * size || - hdr.count > vfio_pci_get_irq_count(vdev, hdr.index)) - return -EINVAL; - - data = kmalloc(hdr.count * size, GFP_KERNEL); - if (!data) - return -ENOMEM; - - if (copy_from_user(data, (void __user *)(arg + minsz), - hdr.count * size)) { - kfree(data); - return -EFAULT; - } - } - - mutex_lock(&vdev->igate); - - ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index, - hdr.start, hdr.count, data); - - mutex_unlock(&vdev->igate); - kfree(data); - - return ret; - - } else if (cmd == VFIO_DEVICE_RESET) - return vdev->reset_works ? - pci_reset_function(vdev->pdev) : -EINVAL; - - return -ENOTTY; -} - -static ssize_t vfio_pci_read(void *device_data, char __user *buf, - size_t count, loff_t *ppos) -{ - unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - struct vfio_pci_device *vdev = device_data; - struct pci_dev *pdev = vdev->pdev; - - if (index >= VFIO_PCI_NUM_REGIONS) - return -EINVAL; - - if (index == VFIO_PCI_CONFIG_REGION_INDEX) - return vfio_pci_config_readwrite(vdev, buf, count, ppos, false); - else if (index == VFIO_PCI_ROM_REGION_INDEX) - return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); - else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) - return vfio_pci_io_readwrite(vdev, buf, count, ppos, false); - else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) - return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); - - return -EINVAL; -} - -static ssize_t vfio_pci_write(void *device_data, const char __user *buf, - size_t count, loff_t *ppos) -{ - unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - struct vfio_pci_device *vdev = device_data; - struct pci_dev *pdev = vdev->pdev; - - if (index >= VFIO_PCI_NUM_REGIONS) - return -EINVAL; - - if (index == VFIO_PCI_CONFIG_REGION_INDEX) - return vfio_pci_config_readwrite(vdev, (char __user *)buf, - count, ppos, true); - else if (index == VFIO_PCI_ROM_REGION_INDEX) - return -EINVAL; - else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) - return vfio_pci_io_readwrite(vdev, (char __user *)buf, - count, ppos, true); - else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) { - return vfio_pci_mem_readwrite(vdev, (char __user *)buf, - count, ppos, true); - } - - return -EINVAL; -} - -static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) -{ - struct vfio_pci_device *vdev = device_data; - struct pci_dev *pdev = vdev->pdev; - unsigned int index; - u64 phys_len, req_len, pgoff, req_start, phys; - int ret; - - index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT); - - if (vma->vm_end < vma->vm_start) - return -EINVAL; - if ((vma->vm_flags & VM_SHARED) == 0) - return -EINVAL; - if (index >= VFIO_PCI_ROM_REGION_INDEX) - return -EINVAL; - if (!(pci_resource_flags(pdev, index) & IORESOURCE_MEM)) - return -EINVAL; - - phys_len = pci_resource_len(pdev, index); - req_len = vma->vm_end - vma->vm_start; - pgoff = vma->vm_pgoff & - ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1); - req_start = pgoff << PAGE_SHIFT; - - if (phys_len < PAGE_SIZE || req_start + req_len > phys_len) - return -EINVAL; - - if (index == vdev->msix_bar) { - /* - * Disallow mmaps overlapping the MSI-X table; users don't - * get to touch this directly. We could find somewhere - * else to map the overlap, but page granularity is only - * a recommendation, not a requirement, so the user needs - * to know which bits are real. Requiring them to mmap - * around the table makes that clear. - */ - - /* If neither entirely above nor below, then it overlaps */ - if (!(req_start >= vdev->msix_offset + vdev->msix_size || - req_start + req_len <= vdev->msix_offset)) - return -EINVAL; - } - - /* - * Even though we don't make use of the barmap for the mmap, - * we need to request the region and the barmap tracks that. - */ - if (!vdev->barmap[index]) { - ret = pci_request_selected_regions(pdev, - 1 << index, "vfio-pci"); - if (ret) - return ret; - - vdev->barmap[index] = pci_iomap(pdev, index, 0); - } - - vma->vm_private_data = vdev; - vma->vm_flags |= (VM_IO | VM_RESERVED); - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - phys = (pci_resource_start(pdev, index) >> PAGE_SHIFT) + pgoff; - - return remap_pfn_range(vma, vma->vm_start, phys, - req_len, vma->vm_page_prot); -} - -static const struct vfio_device_ops vfio_pci_ops = { - .name = "vfio-pci", - .open = vfio_pci_open, - .release = vfio_pci_release, - .ioctl = vfio_pci_ioctl, - .read = vfio_pci_read, - .write = vfio_pci_write, - .mmap = vfio_pci_mmap, -}; - -static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - u8 type; - struct vfio_pci_device *vdev; - struct iommu_group *group; - int ret; - - pci_read_config_byte(pdev, PCI_HEADER_TYPE, &type); - if ((type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL) - return -EINVAL; - - group = iommu_group_get(&pdev->dev); - if (!group) - return -EINVAL; - - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) { - iommu_group_put(group); - return -ENOMEM; - } - - vdev->pdev = pdev; - vdev->irq_type = VFIO_PCI_NUM_IRQS; - mutex_init(&vdev->igate); - spin_lock_init(&vdev->irqlock); - atomic_set(&vdev->refcnt, 0); - - ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev); - if (ret) { - iommu_group_put(group); - kfree(vdev); - } - - return ret; -} - -static void vfio_pci_remove(struct pci_dev *pdev) -{ - struct vfio_pci_device *vdev; - - vdev = vfio_del_group_dev(&pdev->dev); - if (!vdev) - return; - - iommu_group_put(pdev->dev.iommu_group); - kfree(vdev); -} - -static struct pci_driver vfio_pci_driver = { - .name = "vfio-pci", - .id_table = NULL, /* only dynamic ids */ - .probe = vfio_pci_probe, - .remove = vfio_pci_remove, -}; - -static void __exit vfio_pci_cleanup(void) -{ - pci_unregister_driver(&vfio_pci_driver); - vfio_pci_virqfd_exit(); - vfio_pci_uninit_perm_bits(); -} - -static int __init vfio_pci_init(void) -{ - int ret; - - /* Allocate shared config space permision data used by all devices */ - ret = vfio_pci_init_perm_bits(); - if (ret) - return ret; - - /* Start the virqfd cleanup handler */ - ret = vfio_pci_virqfd_init(); - if (ret) - goto out_virqfd; - - /* Register and scan for devices */ - ret = pci_register_driver(&vfio_pci_driver); - if (ret) - goto out_driver; - - return 0; - -out_virqfd: - vfio_pci_virqfd_exit(); -out_driver: - vfio_pci_uninit_perm_bits(); - return ret; -} - -module_init(vfio_pci_init); -module_exit(vfio_pci_cleanup); - -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/trunk/drivers/vfio/pci/vfio_pci_config.c b/trunk/drivers/vfio/pci/vfio_pci_config.c deleted file mode 100644 index 8b8f7d11e102..000000000000 --- a/trunk/drivers/vfio/pci/vfio_pci_config.c +++ /dev/null @@ -1,1540 +0,0 @@ -/* - * VFIO PCI config space virtualization - * - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * Author: Alex Williamson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from original vfio: - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * Author: Tom Lyon, pugs@cisco.com - */ - -/* - * This code handles reading and writing of PCI configuration registers. - * This is hairy because we want to allow a lot of flexibility to the - * user driver, but cannot trust it with all of the config fields. - * Tables determine which fields can be read and written, as well as - * which fields are 'virtualized' - special actions and translations to - * make it appear to the user that he has control, when in fact things - * must be negotiated with the underlying OS. - */ - -#include -#include -#include -#include - -#include "vfio_pci_private.h" - -#define PCI_CFG_SPACE_SIZE 256 - -/* Useful "pseudo" capabilities */ -#define PCI_CAP_ID_BASIC 0 -#define PCI_CAP_ID_INVALID 0xFF - -#define is_bar(offset) \ - ((offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4) || \ - (offset >= PCI_ROM_ADDRESS && offset < PCI_ROM_ADDRESS + 4)) - -/* - * Lengths of PCI Config Capabilities - * 0: Removed from the user visible capability list - * FF: Variable length - */ -static u8 pci_cap_length[] = { - [PCI_CAP_ID_BASIC] = PCI_STD_HEADER_SIZEOF, /* pci config header */ - [PCI_CAP_ID_PM] = PCI_PM_SIZEOF, - [PCI_CAP_ID_AGP] = PCI_AGP_SIZEOF, - [PCI_CAP_ID_VPD] = PCI_CAP_VPD_SIZEOF, - [PCI_CAP_ID_SLOTID] = 0, /* bridge - don't care */ - [PCI_CAP_ID_MSI] = 0xFF, /* 10, 14, 20, or 24 */ - [PCI_CAP_ID_CHSWP] = 0, /* cpci - not yet */ - [PCI_CAP_ID_PCIX] = 0xFF, /* 8 or 24 */ - [PCI_CAP_ID_HT] = 0xFF, /* hypertransport */ - [PCI_CAP_ID_VNDR] = 0xFF, /* variable */ - [PCI_CAP_ID_DBG] = 0, /* debug - don't care */ - [PCI_CAP_ID_CCRC] = 0, /* cpci - not yet */ - [PCI_CAP_ID_SHPC] = 0, /* hotswap - not yet */ - [PCI_CAP_ID_SSVID] = 0, /* bridge - don't care */ - [PCI_CAP_ID_AGP3] = 0, /* AGP8x - not yet */ - [PCI_CAP_ID_SECDEV] = 0, /* secure device not yet */ - [PCI_CAP_ID_EXP] = 0xFF, /* 20 or 44 */ - [PCI_CAP_ID_MSIX] = PCI_CAP_MSIX_SIZEOF, - [PCI_CAP_ID_SATA] = 0xFF, - [PCI_CAP_ID_AF] = PCI_CAP_AF_SIZEOF, -}; - -/* - * Lengths of PCIe/PCI-X Extended Config Capabilities - * 0: Removed or masked from the user visible capabilty list - * FF: Variable length - */ -static u16 pci_ext_cap_length[] = { - [PCI_EXT_CAP_ID_ERR] = PCI_ERR_ROOT_COMMAND, - [PCI_EXT_CAP_ID_VC] = 0xFF, - [PCI_EXT_CAP_ID_DSN] = PCI_EXT_CAP_DSN_SIZEOF, - [PCI_EXT_CAP_ID_PWR] = PCI_EXT_CAP_PWR_SIZEOF, - [PCI_EXT_CAP_ID_RCLD] = 0, /* root only - don't care */ - [PCI_EXT_CAP_ID_RCILC] = 0, /* root only - don't care */ - [PCI_EXT_CAP_ID_RCEC] = 0, /* root only - don't care */ - [PCI_EXT_CAP_ID_MFVC] = 0xFF, - [PCI_EXT_CAP_ID_VC9] = 0xFF, /* same as CAP_ID_VC */ - [PCI_EXT_CAP_ID_RCRB] = 0, /* root only - don't care */ - [PCI_EXT_CAP_ID_VNDR] = 0xFF, - [PCI_EXT_CAP_ID_CAC] = 0, /* obsolete */ - [PCI_EXT_CAP_ID_ACS] = 0xFF, - [PCI_EXT_CAP_ID_ARI] = PCI_EXT_CAP_ARI_SIZEOF, - [PCI_EXT_CAP_ID_ATS] = PCI_EXT_CAP_ATS_SIZEOF, - [PCI_EXT_CAP_ID_SRIOV] = PCI_EXT_CAP_SRIOV_SIZEOF, - [PCI_EXT_CAP_ID_MRIOV] = 0, /* not yet */ - [PCI_EXT_CAP_ID_MCAST] = PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF, - [PCI_EXT_CAP_ID_PRI] = PCI_EXT_CAP_PRI_SIZEOF, - [PCI_EXT_CAP_ID_AMD_XXX] = 0, /* not yet */ - [PCI_EXT_CAP_ID_REBAR] = 0xFF, - [PCI_EXT_CAP_ID_DPA] = 0xFF, - [PCI_EXT_CAP_ID_TPH] = 0xFF, - [PCI_EXT_CAP_ID_LTR] = PCI_EXT_CAP_LTR_SIZEOF, - [PCI_EXT_CAP_ID_SECPCI] = 0, /* not yet */ - [PCI_EXT_CAP_ID_PMUX] = 0, /* not yet */ - [PCI_EXT_CAP_ID_PASID] = 0, /* not yet */ -}; - -/* - * Read/Write Permission Bits - one bit for each bit in capability - * Any field can be read if it exists, but what is read depends on - * whether the field is 'virtualized', or just pass thru to the - * hardware. Any virtualized field is also virtualized for writes. - * Writes are only permitted if they have a 1 bit here. - */ -struct perm_bits { - u8 *virt; /* read/write virtual data, not hw */ - u8 *write; /* writeable bits */ - int (*readfn)(struct vfio_pci_device *vdev, int pos, int count, - struct perm_bits *perm, int offset, __le32 *val); - int (*writefn)(struct vfio_pci_device *vdev, int pos, int count, - struct perm_bits *perm, int offset, __le32 val); -}; - -#define NO_VIRT 0 -#define ALL_VIRT 0xFFFFFFFFU -#define NO_WRITE 0 -#define ALL_WRITE 0xFFFFFFFFU - -static int vfio_user_config_read(struct pci_dev *pdev, int offset, - __le32 *val, int count) -{ - int ret = -EINVAL; - u32 tmp_val = 0; - - switch (count) { - case 1: - { - u8 tmp; - ret = pci_user_read_config_byte(pdev, offset, &tmp); - tmp_val = tmp; - break; - } - case 2: - { - u16 tmp; - ret = pci_user_read_config_word(pdev, offset, &tmp); - tmp_val = tmp; - break; - } - case 4: - ret = pci_user_read_config_dword(pdev, offset, &tmp_val); - break; - } - - *val = cpu_to_le32(tmp_val); - - return pcibios_err_to_errno(ret); -} - -static int vfio_user_config_write(struct pci_dev *pdev, int offset, - __le32 val, int count) -{ - int ret = -EINVAL; - u32 tmp_val = le32_to_cpu(val); - - switch (count) { - case 1: - ret = pci_user_write_config_byte(pdev, offset, tmp_val); - break; - case 2: - ret = pci_user_write_config_word(pdev, offset, tmp_val); - break; - case 4: - ret = pci_user_write_config_dword(pdev, offset, tmp_val); - break; - } - - return pcibios_err_to_errno(ret); -} - -static int vfio_default_config_read(struct vfio_pci_device *vdev, int pos, - int count, struct perm_bits *perm, - int offset, __le32 *val) -{ - __le32 virt = 0; - - memcpy(val, vdev->vconfig + pos, count); - - memcpy(&virt, perm->virt + offset, count); - - /* Any non-virtualized bits? */ - if (cpu_to_le32(~0U >> (32 - (count * 8))) != virt) { - struct pci_dev *pdev = vdev->pdev; - __le32 phys_val = 0; - int ret; - - ret = vfio_user_config_read(pdev, pos, &phys_val, count); - if (ret) - return ret; - - *val = (phys_val & ~virt) | (*val & virt); - } - - return count; -} - -static int vfio_default_config_write(struct vfio_pci_device *vdev, int pos, - int count, struct perm_bits *perm, - int offset, __le32 val) -{ - __le32 virt = 0, write = 0; - - memcpy(&write, perm->write + offset, count); - - if (!write) - return count; /* drop, no writable bits */ - - memcpy(&virt, perm->virt + offset, count); - - /* Virtualized and writable bits go to vconfig */ - if (write & virt) { - __le32 virt_val = 0; - - memcpy(&virt_val, vdev->vconfig + pos, count); - - virt_val &= ~(write & virt); - virt_val |= (val & (write & virt)); - - memcpy(vdev->vconfig + pos, &virt_val, count); - } - - /* Non-virtualzed and writable bits go to hardware */ - if (write & ~virt) { - struct pci_dev *pdev = vdev->pdev; - __le32 phys_val = 0; - int ret; - - ret = vfio_user_config_read(pdev, pos, &phys_val, count); - if (ret) - return ret; - - phys_val &= ~(write & ~virt); - phys_val |= (val & (write & ~virt)); - - ret = vfio_user_config_write(pdev, pos, phys_val, count); - if (ret) - return ret; - } - - return count; -} - -/* Allow direct read from hardware, except for capability next pointer */ -static int vfio_direct_config_read(struct vfio_pci_device *vdev, int pos, - int count, struct perm_bits *perm, - int offset, __le32 *val) -{ - int ret; - - ret = vfio_user_config_read(vdev->pdev, pos, val, count); - if (ret) - return pcibios_err_to_errno(ret); - - if (pos >= PCI_CFG_SPACE_SIZE) { /* Extended cap header mangling */ - if (offset < 4) - memcpy(val, vdev->vconfig + pos, count); - } else if (pos >= PCI_STD_HEADER_SIZEOF) { /* Std cap mangling */ - if (offset == PCI_CAP_LIST_ID && count > 1) - memcpy(val, vdev->vconfig + pos, - min(PCI_CAP_FLAGS, count)); - else if (offset == PCI_CAP_LIST_NEXT) - memcpy(val, vdev->vconfig + pos, 1); - } - - return count; -} - -static int vfio_direct_config_write(struct vfio_pci_device *vdev, int pos, - int count, struct perm_bits *perm, - int offset, __le32 val) -{ - int ret; - - ret = vfio_user_config_write(vdev->pdev, pos, val, count); - if (ret) - return ret; - - return count; -} - -/* Default all regions to read-only, no-virtualization */ -static struct perm_bits cap_perms[PCI_CAP_ID_MAX + 1] = { - [0 ... PCI_CAP_ID_MAX] = { .readfn = vfio_direct_config_read } -}; -static struct perm_bits ecap_perms[PCI_EXT_CAP_ID_MAX + 1] = { - [0 ... PCI_EXT_CAP_ID_MAX] = { .readfn = vfio_direct_config_read } -}; - -static void free_perm_bits(struct perm_bits *perm) -{ - kfree(perm->virt); - kfree(perm->write); - perm->virt = NULL; - perm->write = NULL; -} - -static int alloc_perm_bits(struct perm_bits *perm, int size) -{ - /* - * Round up all permission bits to the next dword, this lets us - * ignore whether a read/write exceeds the defined capability - * structure. We can do this because: - * - Standard config space is already dword aligned - * - Capabilities are all dword alinged (bits 0:1 of next reserved) - * - Express capabilities defined as dword aligned - */ - size = round_up(size, 4); - - /* - * Zero state is - * - All Readable, None Writeable, None Virtualized - */ - perm->virt = kzalloc(size, GFP_KERNEL); - perm->write = kzalloc(size, GFP_KERNEL); - if (!perm->virt || !perm->write) { - free_perm_bits(perm); - return -ENOMEM; - } - - perm->readfn = vfio_default_config_read; - perm->writefn = vfio_default_config_write; - - return 0; -} - -/* - * Helper functions for filling in permission tables - */ -static inline void p_setb(struct perm_bits *p, int off, u8 virt, u8 write) -{ - p->virt[off] = virt; - p->write[off] = write; -} - -/* Handle endian-ness - pci and tables are little-endian */ -static inline void p_setw(struct perm_bits *p, int off, u16 virt, u16 write) -{ - *(__le16 *)(&p->virt[off]) = cpu_to_le16(virt); - *(__le16 *)(&p->write[off]) = cpu_to_le16(write); -} - -/* Handle endian-ness - pci and tables are little-endian */ -static inline void p_setd(struct perm_bits *p, int off, u32 virt, u32 write) -{ - *(__le32 *)(&p->virt[off]) = cpu_to_le32(virt); - *(__le32 *)(&p->write[off]) = cpu_to_le32(write); -} - -/* - * Restore the *real* BARs after we detect a FLR or backdoor reset. - * (backdoor = some device specific technique that we didn't catch) - */ -static void vfio_bar_restore(struct vfio_pci_device *vdev) -{ - struct pci_dev *pdev = vdev->pdev; - u32 *rbar = vdev->rbar; - int i; - - if (pdev->is_virtfn) - return; - - pr_info("%s: %s reset recovery - restoring bars\n", - __func__, dev_name(&pdev->dev)); - - for (i = PCI_BASE_ADDRESS_0; i <= PCI_BASE_ADDRESS_5; i += 4, rbar++) - pci_user_write_config_dword(pdev, i, *rbar); - - pci_user_write_config_dword(pdev, PCI_ROM_ADDRESS, *rbar); -} - -static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar) -{ - unsigned long flags = pci_resource_flags(pdev, bar); - u32 val; - - if (flags & IORESOURCE_IO) - return cpu_to_le32(PCI_BASE_ADDRESS_SPACE_IO); - - val = PCI_BASE_ADDRESS_SPACE_MEMORY; - - if (flags & IORESOURCE_PREFETCH) - val |= PCI_BASE_ADDRESS_MEM_PREFETCH; - - if (flags & IORESOURCE_MEM_64) - val |= PCI_BASE_ADDRESS_MEM_TYPE_64; - - return cpu_to_le32(val); -} - -/* - * Pretend we're hardware and tweak the values of the *virtual* PCI BARs - * to reflect the hardware capabilities. This implements BAR sizing. - */ -static void vfio_bar_fixup(struct vfio_pci_device *vdev) -{ - struct pci_dev *pdev = vdev->pdev; - int i; - __le32 *bar; - u64 mask; - - bar = (__le32 *)&vdev->vconfig[PCI_BASE_ADDRESS_0]; - - for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++, bar++) { - if (!pci_resource_start(pdev, i)) { - *bar = 0; /* Unmapped by host = unimplemented to user */ - continue; - } - - mask = ~(pci_resource_len(pdev, i) - 1); - - *bar &= cpu_to_le32((u32)mask); - *bar |= vfio_generate_bar_flags(pdev, i); - - if (*bar & cpu_to_le32(PCI_BASE_ADDRESS_MEM_TYPE_64)) { - bar++; - *bar &= cpu_to_le32((u32)(mask >> 32)); - i++; - } - } - - bar = (__le32 *)&vdev->vconfig[PCI_ROM_ADDRESS]; - - /* - * NB. we expose the actual BAR size here, regardless of whether - * we can read it. When we report the REGION_INFO for the ROM - * we report what PCI tells us is the actual ROM size. - */ - if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) { - mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1); - mask |= PCI_ROM_ADDRESS_ENABLE; - *bar &= cpu_to_le32((u32)mask); - } else - *bar = 0; - - vdev->bardirty = false; -} - -static int vfio_basic_config_read(struct vfio_pci_device *vdev, int pos, - int count, struct perm_bits *perm, - int offset, __le32 *val) -{ - if (is_bar(offset)) /* pos == offset for basic config */ - vfio_bar_fixup(vdev); - - count = vfio_default_config_read(vdev, pos, count, perm, offset, val); - - /* Mask in virtual memory enable for SR-IOV devices */ - if (offset == PCI_COMMAND && vdev->pdev->is_virtfn) { - u16 cmd = le16_to_cpu(*(__le16 *)&vdev->vconfig[PCI_COMMAND]); - u32 tmp_val = le32_to_cpu(*val); - - tmp_val |= cmd & PCI_COMMAND_MEMORY; - *val = cpu_to_le32(tmp_val); - } - - return count; -} - -static int vfio_basic_config_write(struct vfio_pci_device *vdev, int pos, - int count, struct perm_bits *perm, - int offset, __le32 val) -{ - struct pci_dev *pdev = vdev->pdev; - __le16 *virt_cmd; - u16 new_cmd = 0; - int ret; - - virt_cmd = (__le16 *)&vdev->vconfig[PCI_COMMAND]; - - if (offset == PCI_COMMAND) { - bool phys_mem, virt_mem, new_mem, phys_io, virt_io, new_io; - u16 phys_cmd; - - ret = pci_user_read_config_word(pdev, PCI_COMMAND, &phys_cmd); - if (ret) - return ret; - - new_cmd = le32_to_cpu(val); - - phys_mem = !!(phys_cmd & PCI_COMMAND_MEMORY); - virt_mem = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_MEMORY); - new_mem = !!(new_cmd & PCI_COMMAND_MEMORY); - - phys_io = !!(phys_cmd & PCI_COMMAND_IO); - virt_io = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_IO); - new_io = !!(new_cmd & PCI_COMMAND_IO); - - /* - * If the user is writing mem/io enable (new_mem/io) and we - * think it's already enabled (virt_mem/io), but the hardware - * shows it disabled (phys_mem/io, then the device has - * undergone some kind of backdoor reset and needs to be - * restored before we allow it to enable the bars. - * SR-IOV devices will trigger this, but we catch them later - */ - if ((new_mem && virt_mem && !phys_mem) || - (new_io && virt_io && !phys_io)) - vfio_bar_restore(vdev); - } - - count = vfio_default_config_write(vdev, pos, count, perm, offset, val); - if (count < 0) - return count; - - /* - * Save current memory/io enable bits in vconfig to allow for - * the test above next time. - */ - if (offset == PCI_COMMAND) { - u16 mask = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; - - *virt_cmd &= cpu_to_le16(~mask); - *virt_cmd |= cpu_to_le16(new_cmd & mask); - } - - /* Emulate INTx disable */ - if (offset >= PCI_COMMAND && offset <= PCI_COMMAND + 1) { - bool virt_intx_disable; - - virt_intx_disable = !!(le16_to_cpu(*virt_cmd) & - PCI_COMMAND_INTX_DISABLE); - - if (virt_intx_disable && !vdev->virq_disabled) { - vdev->virq_disabled = true; - vfio_pci_intx_mask(vdev); - } else if (!virt_intx_disable && vdev->virq_disabled) { - vdev->virq_disabled = false; - vfio_pci_intx_unmask(vdev); - } - } - - if (is_bar(offset)) - vdev->bardirty = true; - - return count; -} - -/* Permissions for the Basic PCI Header */ -static int __init init_pci_cap_basic_perm(struct perm_bits *perm) -{ - if (alloc_perm_bits(perm, PCI_STD_HEADER_SIZEOF)) - return -ENOMEM; - - perm->readfn = vfio_basic_config_read; - perm->writefn = vfio_basic_config_write; - - /* Virtualized for SR-IOV functions, which just have FFFF */ - p_setw(perm, PCI_VENDOR_ID, (u16)ALL_VIRT, NO_WRITE); - p_setw(perm, PCI_DEVICE_ID, (u16)ALL_VIRT, NO_WRITE); - - /* - * Virtualize INTx disable, we use it internally for interrupt - * control and can emulate it for non-PCI 2.3 devices. - */ - p_setw(perm, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE, (u16)ALL_WRITE); - - /* Virtualize capability list, we might want to skip/disable */ - p_setw(perm, PCI_STATUS, PCI_STATUS_CAP_LIST, NO_WRITE); - - /* No harm to write */ - p_setb(perm, PCI_CACHE_LINE_SIZE, NO_VIRT, (u8)ALL_WRITE); - p_setb(perm, PCI_LATENCY_TIMER, NO_VIRT, (u8)ALL_WRITE); - p_setb(perm, PCI_BIST, NO_VIRT, (u8)ALL_WRITE); - - /* Virtualize all bars, can't touch the real ones */ - p_setd(perm, PCI_BASE_ADDRESS_0, ALL_VIRT, ALL_WRITE); - p_setd(perm, PCI_BASE_ADDRESS_1, ALL_VIRT, ALL_WRITE); - p_setd(perm, PCI_BASE_ADDRESS_2, ALL_VIRT, ALL_WRITE); - p_setd(perm, PCI_BASE_ADDRESS_3, ALL_VIRT, ALL_WRITE); - p_setd(perm, PCI_BASE_ADDRESS_4, ALL_VIRT, ALL_WRITE); - p_setd(perm, PCI_BASE_ADDRESS_5, ALL_VIRT, ALL_WRITE); - p_setd(perm, PCI_ROM_ADDRESS, ALL_VIRT, ALL_WRITE); - - /* Allow us to adjust capability chain */ - p_setb(perm, PCI_CAPABILITY_LIST, (u8)ALL_VIRT, NO_WRITE); - - /* Sometimes used by sw, just virtualize */ - p_setb(perm, PCI_INTERRUPT_LINE, (u8)ALL_VIRT, (u8)ALL_WRITE); - return 0; -} - -/* Permissions for the Power Management capability */ -static int __init init_pci_cap_pm_perm(struct perm_bits *perm) -{ - if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM])) - return -ENOMEM; - - /* - * We always virtualize the next field so we can remove - * capabilities from the chain if we want to. - */ - p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); - - /* - * Power management is defined *per function*, - * so we let the user write this - */ - p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE); - return 0; -} - -/* Permissions for PCI-X capability */ -static int __init init_pci_cap_pcix_perm(struct perm_bits *perm) -{ - /* Alloc 24, but only 8 are used in v0 */ - if (alloc_perm_bits(perm, PCI_CAP_PCIX_SIZEOF_V2)) - return -ENOMEM; - - p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); - - p_setw(perm, PCI_X_CMD, NO_VIRT, (u16)ALL_WRITE); - p_setd(perm, PCI_X_ECC_CSR, NO_VIRT, ALL_WRITE); - return 0; -} - -/* Permissions for PCI Express capability */ -static int __init init_pci_cap_exp_perm(struct perm_bits *perm) -{ - /* Alloc larger of two possible sizes */ - if (alloc_perm_bits(perm, PCI_CAP_EXP_ENDPOINT_SIZEOF_V2)) - return -ENOMEM; - - p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); - - /* - * Allow writes to device control fields (includes FLR!) - * but not to devctl_phantom which could confuse IOMMU - * or to the ARI bit in devctl2 which is set at probe time - */ - p_setw(perm, PCI_EXP_DEVCTL, NO_VIRT, ~PCI_EXP_DEVCTL_PHANTOM); - p_setw(perm, PCI_EXP_DEVCTL2, NO_VIRT, ~PCI_EXP_DEVCTL2_ARI); - return 0; -} - -/* Permissions for Advanced Function capability */ -static int __init init_pci_cap_af_perm(struct perm_bits *perm) -{ - if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_AF])) - return -ENOMEM; - - p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); - p_setb(perm, PCI_AF_CTRL, NO_VIRT, PCI_AF_CTRL_FLR); - return 0; -} - -/* Permissions for Advanced Error Reporting extended capability */ -static int __init init_pci_ext_cap_err_perm(struct perm_bits *perm) -{ - u32 mask; - - if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_ERR])) - return -ENOMEM; - - /* - * Virtualize the first dword of all express capabilities - * because it includes the next pointer. This lets us later - * remove capabilities from the chain if we need to. - */ - p_setd(perm, 0, ALL_VIRT, NO_WRITE); - - /* Writable bits mask */ - mask = PCI_ERR_UNC_TRAIN | /* Training */ - PCI_ERR_UNC_DLP | /* Data Link Protocol */ - PCI_ERR_UNC_SURPDN | /* Surprise Down */ - PCI_ERR_UNC_POISON_TLP | /* Poisoned TLP */ - PCI_ERR_UNC_FCP | /* Flow Control Protocol */ - PCI_ERR_UNC_COMP_TIME | /* Completion Timeout */ - PCI_ERR_UNC_COMP_ABORT | /* Completer Abort */ - PCI_ERR_UNC_UNX_COMP | /* Unexpected Completion */ - PCI_ERR_UNC_RX_OVER | /* Receiver Overflow */ - PCI_ERR_UNC_MALF_TLP | /* Malformed TLP */ - PCI_ERR_UNC_ECRC | /* ECRC Error Status */ - PCI_ERR_UNC_UNSUP | /* Unsupported Request */ - PCI_ERR_UNC_ACSV | /* ACS Violation */ - PCI_ERR_UNC_INTN | /* internal error */ - PCI_ERR_UNC_MCBTLP | /* MC blocked TLP */ - PCI_ERR_UNC_ATOMEG | /* Atomic egress blocked */ - PCI_ERR_UNC_TLPPRE; /* TLP prefix blocked */ - p_setd(perm, PCI_ERR_UNCOR_STATUS, NO_VIRT, mask); - p_setd(perm, PCI_ERR_UNCOR_MASK, NO_VIRT, mask); - p_setd(perm, PCI_ERR_UNCOR_SEVER, NO_VIRT, mask); - - mask = PCI_ERR_COR_RCVR | /* Receiver Error Status */ - PCI_ERR_COR_BAD_TLP | /* Bad TLP Status */ - PCI_ERR_COR_BAD_DLLP | /* Bad DLLP Status */ - PCI_ERR_COR_REP_ROLL | /* REPLAY_NUM Rollover */ - PCI_ERR_COR_REP_TIMER | /* Replay Timer Timeout */ - PCI_ERR_COR_ADV_NFAT | /* Advisory Non-Fatal */ - PCI_ERR_COR_INTERNAL | /* Corrected Internal */ - PCI_ERR_COR_LOG_OVER; /* Header Log Overflow */ - p_setd(perm, PCI_ERR_COR_STATUS, NO_VIRT, mask); - p_setd(perm, PCI_ERR_COR_MASK, NO_VIRT, mask); - - mask = PCI_ERR_CAP_ECRC_GENE | /* ECRC Generation Enable */ - PCI_ERR_CAP_ECRC_CHKE; /* ECRC Check Enable */ - p_setd(perm, PCI_ERR_CAP, NO_VIRT, mask); - return 0; -} - -/* Permissions for Power Budgeting extended capability */ -static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm) -{ - if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_PWR])) - return -ENOMEM; - - p_setd(perm, 0, ALL_VIRT, NO_WRITE); - - /* Writing the data selector is OK, the info is still read-only */ - p_setb(perm, PCI_PWR_DATA, NO_VIRT, (u8)ALL_WRITE); - return 0; -} - -/* - * Initialize the shared permission tables - */ -void vfio_pci_uninit_perm_bits(void) -{ - free_perm_bits(&cap_perms[PCI_CAP_ID_BASIC]); - - free_perm_bits(&cap_perms[PCI_CAP_ID_PM]); - free_perm_bits(&cap_perms[PCI_CAP_ID_PCIX]); - free_perm_bits(&cap_perms[PCI_CAP_ID_EXP]); - free_perm_bits(&cap_perms[PCI_CAP_ID_AF]); - - free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_ERR]); - free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_PWR]); -} - -int __init vfio_pci_init_perm_bits(void) -{ - int ret; - - /* Basic config space */ - ret = init_pci_cap_basic_perm(&cap_perms[PCI_CAP_ID_BASIC]); - - /* Capabilities */ - ret |= init_pci_cap_pm_perm(&cap_perms[PCI_CAP_ID_PM]); - cap_perms[PCI_CAP_ID_VPD].writefn = vfio_direct_config_write; - ret |= init_pci_cap_pcix_perm(&cap_perms[PCI_CAP_ID_PCIX]); - cap_perms[PCI_CAP_ID_VNDR].writefn = vfio_direct_config_write; - ret |= init_pci_cap_exp_perm(&cap_perms[PCI_CAP_ID_EXP]); - ret |= init_pci_cap_af_perm(&cap_perms[PCI_CAP_ID_AF]); - - /* Extended capabilities */ - ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]); - ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]); - ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_direct_config_write; - - if (ret) - vfio_pci_uninit_perm_bits(); - - return ret; -} - -static int vfio_find_cap_start(struct vfio_pci_device *vdev, int pos) -{ - u8 cap; - int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE : - PCI_STD_HEADER_SIZEOF; - base /= 4; - pos /= 4; - - cap = vdev->pci_config_map[pos]; - - if (cap == PCI_CAP_ID_BASIC) - return 0; - - /* XXX Can we have to abutting capabilities of the same type? */ - while (pos - 1 >= base && vdev->pci_config_map[pos - 1] == cap) - pos--; - - return pos * 4; -} - -static int vfio_msi_config_read(struct vfio_pci_device *vdev, int pos, - int count, struct perm_bits *perm, - int offset, __le32 *val) -{ - /* Update max available queue size from msi_qmax */ - if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) { - __le16 *flags; - int start; - - start = vfio_find_cap_start(vdev, pos); - - flags = (__le16 *)&vdev->vconfig[start]; - - *flags &= cpu_to_le16(~PCI_MSI_FLAGS_QMASK); - *flags |= cpu_to_le16(vdev->msi_qmax << 1); - } - - return vfio_default_config_read(vdev, pos, count, perm, offset, val); -} - -static int vfio_msi_config_write(struct vfio_pci_device *vdev, int pos, - int count, struct perm_bits *perm, - int offset, __le32 val) -{ - count = vfio_default_config_write(vdev, pos, count, perm, offset, val); - if (count < 0) - return count; - - /* Fixup and write configured queue size and enable to hardware */ - if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) { - __le16 *pflags; - u16 flags; - int start, ret; - - start = vfio_find_cap_start(vdev, pos); - - pflags = (__le16 *)&vdev->vconfig[start + PCI_MSI_FLAGS]; - - flags = le16_to_cpu(*pflags); - - /* MSI is enabled via ioctl */ - if (!is_msi(vdev)) - flags &= ~PCI_MSI_FLAGS_ENABLE; - - /* Check queue size */ - if ((flags & PCI_MSI_FLAGS_QSIZE) >> 4 > vdev->msi_qmax) { - flags &= ~PCI_MSI_FLAGS_QSIZE; - flags |= vdev->msi_qmax << 4; - } - - /* Write back to virt and to hardware */ - *pflags = cpu_to_le16(flags); - ret = pci_user_write_config_word(vdev->pdev, - start + PCI_MSI_FLAGS, - flags); - if (ret) - return pcibios_err_to_errno(ret); - } - - return count; -} - -/* - * MSI determination is per-device, so this routine gets used beyond - * initialization time. Don't add __init - */ -static int init_pci_cap_msi_perm(struct perm_bits *perm, int len, u16 flags) -{ - if (alloc_perm_bits(perm, len)) - return -ENOMEM; - - perm->readfn = vfio_msi_config_read; - perm->writefn = vfio_msi_config_write; - - p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); - - /* - * The upper byte of the control register is reserved, - * just setup the lower byte. - */ - p_setb(perm, PCI_MSI_FLAGS, (u8)ALL_VIRT, (u8)ALL_WRITE); - p_setd(perm, PCI_MSI_ADDRESS_LO, ALL_VIRT, ALL_WRITE); - if (flags & PCI_MSI_FLAGS_64BIT) { - p_setd(perm, PCI_MSI_ADDRESS_HI, ALL_VIRT, ALL_WRITE); - p_setw(perm, PCI_MSI_DATA_64, (u16)ALL_VIRT, (u16)ALL_WRITE); - if (flags & PCI_MSI_FLAGS_MASKBIT) { - p_setd(perm, PCI_MSI_MASK_64, NO_VIRT, ALL_WRITE); - p_setd(perm, PCI_MSI_PENDING_64, NO_VIRT, ALL_WRITE); - } - } else { - p_setw(perm, PCI_MSI_DATA_32, (u16)ALL_VIRT, (u16)ALL_WRITE); - if (flags & PCI_MSI_FLAGS_MASKBIT) { - p_setd(perm, PCI_MSI_MASK_32, NO_VIRT, ALL_WRITE); - p_setd(perm, PCI_MSI_PENDING_32, NO_VIRT, ALL_WRITE); - } - } - return 0; -} - -/* Determine MSI CAP field length; initialize msi_perms on 1st call per vdev */ -static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos) -{ - struct pci_dev *pdev = vdev->pdev; - int len, ret; - u16 flags; - - ret = pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &flags); - if (ret) - return pcibios_err_to_errno(ret); - - len = 10; /* Minimum size */ - if (flags & PCI_MSI_FLAGS_64BIT) - len += 4; - if (flags & PCI_MSI_FLAGS_MASKBIT) - len += 10; - - if (vdev->msi_perm) - return len; - - vdev->msi_perm = kmalloc(sizeof(struct perm_bits), GFP_KERNEL); - if (!vdev->msi_perm) - return -ENOMEM; - - ret = init_pci_cap_msi_perm(vdev->msi_perm, len, flags); - if (ret) - return ret; - - return len; -} - -/* Determine extended capability length for VC (2 & 9) and MFVC */ -static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos) -{ - struct pci_dev *pdev = vdev->pdev; - u32 tmp; - int ret, evcc, phases, vc_arb; - int len = PCI_CAP_VC_BASE_SIZEOF; - - ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp); - if (ret) - return pcibios_err_to_errno(ret); - - evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */ - ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp); - if (ret) - return pcibios_err_to_errno(ret); - - if (tmp & PCI_VC_REG2_128_PHASE) - phases = 128; - else if (tmp & PCI_VC_REG2_64_PHASE) - phases = 64; - else if (tmp & PCI_VC_REG2_32_PHASE) - phases = 32; - else - phases = 0; - - vc_arb = phases * 4; - - /* - * Port arbitration tables are root & switch only; - * function arbitration tables are function 0 only. - * In either case, we'll never let user write them so - * we don't care how big they are - */ - len += (1 + evcc) * PCI_CAP_VC_PER_VC_SIZEOF; - if (vc_arb) { - len = round_up(len, 16); - len += vc_arb / 8; - } - return len; -} - -static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos) -{ - struct pci_dev *pdev = vdev->pdev; - u16 word; - u8 byte; - int ret; - - switch (cap) { - case PCI_CAP_ID_MSI: - return vfio_msi_cap_len(vdev, pos); - case PCI_CAP_ID_PCIX: - ret = pci_read_config_word(pdev, pos + PCI_X_CMD, &word); - if (ret) - return pcibios_err_to_errno(ret); - - if (PCI_X_CMD_VERSION(word)) { - vdev->extended_caps = true; - return PCI_CAP_PCIX_SIZEOF_V2; - } else - return PCI_CAP_PCIX_SIZEOF_V0; - case PCI_CAP_ID_VNDR: - /* length follows next field */ - ret = pci_read_config_byte(pdev, pos + PCI_CAP_FLAGS, &byte); - if (ret) - return pcibios_err_to_errno(ret); - - return byte; - case PCI_CAP_ID_EXP: - /* length based on version */ - ret = pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &word); - if (ret) - return pcibios_err_to_errno(ret); - - if ((word & PCI_EXP_FLAGS_VERS) == 1) - return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1; - else { - vdev->extended_caps = true; - return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2; - } - case PCI_CAP_ID_HT: - ret = pci_read_config_byte(pdev, pos + 3, &byte); - if (ret) - return pcibios_err_to_errno(ret); - - return (byte & HT_3BIT_CAP_MASK) ? - HT_CAP_SIZEOF_SHORT : HT_CAP_SIZEOF_LONG; - case PCI_CAP_ID_SATA: - ret = pci_read_config_byte(pdev, pos + PCI_SATA_REGS, &byte); - if (ret) - return pcibios_err_to_errno(ret); - - byte &= PCI_SATA_REGS_MASK; - if (byte == PCI_SATA_REGS_INLINE) - return PCI_SATA_SIZEOF_LONG; - else - return PCI_SATA_SIZEOF_SHORT; - default: - pr_warn("%s: %s unknown length for pci cap 0x%x@0x%x\n", - dev_name(&pdev->dev), __func__, cap, pos); - } - - return 0; -} - -static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos) -{ - struct pci_dev *pdev = vdev->pdev; - u8 byte; - u32 dword; - int ret; - - switch (ecap) { - case PCI_EXT_CAP_ID_VNDR: - ret = pci_read_config_dword(pdev, epos + PCI_VSEC_HDR, &dword); - if (ret) - return pcibios_err_to_errno(ret); - - return dword >> PCI_VSEC_HDR_LEN_SHIFT; - case PCI_EXT_CAP_ID_VC: - case PCI_EXT_CAP_ID_VC9: - case PCI_EXT_CAP_ID_MFVC: - return vfio_vc_cap_len(vdev, epos); - case PCI_EXT_CAP_ID_ACS: - ret = pci_read_config_byte(pdev, epos + PCI_ACS_CAP, &byte); - if (ret) - return pcibios_err_to_errno(ret); - - if (byte & PCI_ACS_EC) { - int bits; - - ret = pci_read_config_byte(pdev, - epos + PCI_ACS_EGRESS_BITS, - &byte); - if (ret) - return pcibios_err_to_errno(ret); - - bits = byte ? round_up(byte, 32) : 256; - return 8 + (bits / 8); - } - return 8; - - case PCI_EXT_CAP_ID_REBAR: - ret = pci_read_config_byte(pdev, epos + PCI_REBAR_CTRL, &byte); - if (ret) - return pcibios_err_to_errno(ret); - - byte &= PCI_REBAR_CTRL_NBAR_MASK; - byte >>= PCI_REBAR_CTRL_NBAR_SHIFT; - - return 4 + (byte * 8); - case PCI_EXT_CAP_ID_DPA: - ret = pci_read_config_byte(pdev, epos + PCI_DPA_CAP, &byte); - if (ret) - return pcibios_err_to_errno(ret); - - byte &= PCI_DPA_CAP_SUBSTATE_MASK; - byte = round_up(byte + 1, 4); - return PCI_DPA_BASE_SIZEOF + byte; - case PCI_EXT_CAP_ID_TPH: - ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword); - if (ret) - return pcibios_err_to_errno(ret); - - if ((dword & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) { - int sts; - - sts = byte & PCI_TPH_CAP_ST_MASK; - sts >>= PCI_TPH_CAP_ST_SHIFT; - return PCI_TPH_BASE_SIZEOF + round_up(sts * 2, 4); - } - return PCI_TPH_BASE_SIZEOF; - default: - pr_warn("%s: %s unknown length for pci ecap 0x%x@0x%x\n", - dev_name(&pdev->dev), __func__, ecap, epos); - } - - return 0; -} - -static int vfio_fill_vconfig_bytes(struct vfio_pci_device *vdev, - int offset, int size) -{ - struct pci_dev *pdev = vdev->pdev; - int ret = 0; - - /* - * We try to read physical config space in the largest chunks - * we can, assuming that all of the fields support dword access. - * pci_save_state() makes this same assumption and seems to do ok. - */ - while (size) { - int filled; - - if (size >= 4 && !(offset % 4)) { - __le32 *dwordp = (__le32 *)&vdev->vconfig[offset]; - u32 dword; - - ret = pci_read_config_dword(pdev, offset, &dword); - if (ret) - return ret; - *dwordp = cpu_to_le32(dword); - filled = 4; - } else if (size >= 2 && !(offset % 2)) { - __le16 *wordp = (__le16 *)&vdev->vconfig[offset]; - u16 word; - - ret = pci_read_config_word(pdev, offset, &word); - if (ret) - return ret; - *wordp = cpu_to_le16(word); - filled = 2; - } else { - u8 *byte = &vdev->vconfig[offset]; - ret = pci_read_config_byte(pdev, offset, byte); - if (ret) - return ret; - filled = 1; - } - - offset += filled; - size -= filled; - } - - return ret; -} - -static int vfio_cap_init(struct vfio_pci_device *vdev) -{ - struct pci_dev *pdev = vdev->pdev; - u8 *map = vdev->pci_config_map; - u16 status; - u8 pos, *prev, cap; - int loops, ret, caps = 0; - - /* Any capabilities? */ - ret = pci_read_config_word(pdev, PCI_STATUS, &status); - if (ret) - return ret; - - if (!(status & PCI_STATUS_CAP_LIST)) - return 0; /* Done */ - - ret = pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pos); - if (ret) - return ret; - - /* Mark the previous position in case we want to skip a capability */ - prev = &vdev->vconfig[PCI_CAPABILITY_LIST]; - - /* We can bound our loop, capabilities are dword aligned */ - loops = (PCI_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF) / PCI_CAP_SIZEOF; - while (pos && loops--) { - u8 next; - int i, len = 0; - - ret = pci_read_config_byte(pdev, pos, &cap); - if (ret) - return ret; - - ret = pci_read_config_byte(pdev, - pos + PCI_CAP_LIST_NEXT, &next); - if (ret) - return ret; - - if (cap <= PCI_CAP_ID_MAX) { - len = pci_cap_length[cap]; - if (len == 0xFF) { /* Variable length */ - len = vfio_cap_len(vdev, cap, pos); - if (len < 0) - return len; - } - } - - if (!len) { - pr_info("%s: %s hiding cap 0x%x\n", - __func__, dev_name(&pdev->dev), cap); - *prev = next; - pos = next; - continue; - } - - /* Sanity check, do we overlap other capabilities? */ - for (i = 0; i < len; i += 4) { - if (likely(map[(pos + i) / 4] == PCI_CAP_ID_INVALID)) - continue; - - pr_warn("%s: %s pci config conflict @0x%x, was cap 0x%x now cap 0x%x\n", - __func__, dev_name(&pdev->dev), - pos + i, map[pos + i], cap); - } - - memset(map + (pos / 4), cap, len / 4); - ret = vfio_fill_vconfig_bytes(vdev, pos, len); - if (ret) - return ret; - - prev = &vdev->vconfig[pos + PCI_CAP_LIST_NEXT]; - pos = next; - caps++; - } - - /* If we didn't fill any capabilities, clear the status flag */ - if (!caps) { - __le16 *vstatus = (__le16 *)&vdev->vconfig[PCI_STATUS]; - *vstatus &= ~cpu_to_le16(PCI_STATUS_CAP_LIST); - } - - return 0; -} - -static int vfio_ecap_init(struct vfio_pci_device *vdev) -{ - struct pci_dev *pdev = vdev->pdev; - u8 *map = vdev->pci_config_map; - u16 epos; - __le32 *prev = NULL; - int loops, ret, ecaps = 0; - - if (!vdev->extended_caps) - return 0; - - epos = PCI_CFG_SPACE_SIZE; - - loops = (pdev->cfg_size - PCI_CFG_SPACE_SIZE) / PCI_CAP_SIZEOF; - - while (loops-- && epos >= PCI_CFG_SPACE_SIZE) { - u32 header; - u16 ecap; - int i, len = 0; - bool hidden = false; - - ret = pci_read_config_dword(pdev, epos, &header); - if (ret) - return ret; - - ecap = PCI_EXT_CAP_ID(header); - - if (ecap <= PCI_EXT_CAP_ID_MAX) { - len = pci_ext_cap_length[ecap]; - if (len == 0xFF) { - len = vfio_ext_cap_len(vdev, ecap, epos); - if (len < 0) - return ret; - } - } - - if (!len) { - pr_info("%s: %s hiding ecap 0x%x@0x%x\n", - __func__, dev_name(&pdev->dev), ecap, epos); - - /* If not the first in the chain, we can skip over it */ - if (prev) { - u32 val = epos = PCI_EXT_CAP_NEXT(header); - *prev &= cpu_to_le32(~(0xffcU << 20)); - *prev |= cpu_to_le32(val << 20); - continue; - } - - /* - * Otherwise, fill in a placeholder, the direct - * readfn will virtualize this automatically - */ - len = PCI_CAP_SIZEOF; - hidden = true; - } - - for (i = 0; i < len; i += 4) { - if (likely(map[(epos + i) / 4] == PCI_CAP_ID_INVALID)) - continue; - - pr_warn("%s: %s pci config conflict @0x%x, was ecap 0x%x now ecap 0x%x\n", - __func__, dev_name(&pdev->dev), - epos + i, map[epos + i], ecap); - } - - /* - * Even though ecap is 2 bytes, we're currently a long way - * from exceeding 1 byte capabilities. If we ever make it - * up to 0xFF we'll need to up this to a two-byte, byte map. - */ - BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID); - - memset(map + (epos / 4), ecap, len / 4); - ret = vfio_fill_vconfig_bytes(vdev, epos, len); - if (ret) - return ret; - - /* - * If we're just using this capability to anchor the list, - * hide the real ID. Only count real ecaps. XXX PCI spec - * indicates to use cap id = 0, version = 0, next = 0 if - * ecaps are absent, hope users check all the way to next. - */ - if (hidden) - *(__le32 *)&vdev->vconfig[epos] &= - cpu_to_le32((0xffcU << 20)); - else - ecaps++; - - prev = (__le32 *)&vdev->vconfig[epos]; - epos = PCI_EXT_CAP_NEXT(header); - } - - if (!ecaps) - *(u32 *)&vdev->vconfig[PCI_CFG_SPACE_SIZE] = 0; - - return 0; -} - -/* - * For each device we allocate a pci_config_map that indicates the - * capability occupying each dword and thus the struct perm_bits we - * use for read and write. We also allocate a virtualized config - * space which tracks reads and writes to bits that we emulate for - * the user. Initial values filled from device. - * - * Using shared stuct perm_bits between all vfio-pci devices saves - * us from allocating cfg_size buffers for virt and write for every - * device. We could remove vconfig and allocate individual buffers - * for each area requring emulated bits, but the array of pointers - * would be comparable in size (at least for standard config space). - */ -int vfio_config_init(struct vfio_pci_device *vdev) -{ - struct pci_dev *pdev = vdev->pdev; - u8 *map, *vconfig; - int ret; - - /* - * Config space, caps and ecaps are all dword aligned, so we can - * use one byte per dword to record the type. - */ - map = kmalloc(pdev->cfg_size / 4, GFP_KERNEL); - if (!map) - return -ENOMEM; - - vconfig = kmalloc(pdev->cfg_size, GFP_KERNEL); - if (!vconfig) { - kfree(map); - return -ENOMEM; - } - - vdev->pci_config_map = map; - vdev->vconfig = vconfig; - - memset(map, PCI_CAP_ID_BASIC, PCI_STD_HEADER_SIZEOF / 4); - memset(map + (PCI_STD_HEADER_SIZEOF / 4), PCI_CAP_ID_INVALID, - (pdev->cfg_size - PCI_STD_HEADER_SIZEOF) / 4); - - ret = vfio_fill_vconfig_bytes(vdev, 0, PCI_STD_HEADER_SIZEOF); - if (ret) - goto out; - - vdev->bardirty = true; - - /* - * XXX can we just pci_load_saved_state/pci_restore_state? - * may need to rebuild vconfig after that - */ - - /* For restore after reset */ - vdev->rbar[0] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_0]); - vdev->rbar[1] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_1]); - vdev->rbar[2] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_2]); - vdev->rbar[3] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_3]); - vdev->rbar[4] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_4]); - vdev->rbar[5] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_5]); - vdev->rbar[6] = le32_to_cpu(*(__le32 *)&vconfig[PCI_ROM_ADDRESS]); - - if (pdev->is_virtfn) { - *(__le16 *)&vconfig[PCI_VENDOR_ID] = cpu_to_le16(pdev->vendor); - *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device); - } - - ret = vfio_cap_init(vdev); - if (ret) - goto out; - - ret = vfio_ecap_init(vdev); - if (ret) - goto out; - - return 0; - -out: - kfree(map); - vdev->pci_config_map = NULL; - kfree(vconfig); - vdev->vconfig = NULL; - return pcibios_err_to_errno(ret); -} - -void vfio_config_free(struct vfio_pci_device *vdev) -{ - kfree(vdev->vconfig); - vdev->vconfig = NULL; - kfree(vdev->pci_config_map); - vdev->pci_config_map = NULL; - kfree(vdev->msi_perm); - vdev->msi_perm = NULL; -} - -static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf, - size_t count, loff_t *ppos, bool iswrite) -{ - struct pci_dev *pdev = vdev->pdev; - struct perm_bits *perm; - __le32 val = 0; - int cap_start = 0, offset; - u8 cap_id; - ssize_t ret = count; - - if (*ppos < 0 || *ppos + count > pdev->cfg_size) - return -EFAULT; - - /* - * gcc can't seem to figure out we're a static function, only called - * with count of 1/2/4 and hits copy_from_user_overflow without this. - */ - if (count > sizeof(val)) - return -EINVAL; - - cap_id = vdev->pci_config_map[*ppos / 4]; - - if (cap_id == PCI_CAP_ID_INVALID) { - if (iswrite) - return ret; /* drop */ - - /* - * Per PCI spec 3.0, section 6.1, reads from reserved and - * unimplemented registers return 0 - */ - if (copy_to_user(buf, &val, count)) - return -EFAULT; - - return ret; - } - - /* - * All capabilities are minimum 4 bytes and aligned on dword - * boundaries. Since we don't support unaligned accesses, we're - * only ever accessing a single capability. - */ - if (*ppos >= PCI_CFG_SPACE_SIZE) { - WARN_ON(cap_id > PCI_EXT_CAP_ID_MAX); - - perm = &ecap_perms[cap_id]; - cap_start = vfio_find_cap_start(vdev, *ppos); - - } else { - WARN_ON(cap_id > PCI_CAP_ID_MAX); - - perm = &cap_perms[cap_id]; - - if (cap_id == PCI_CAP_ID_MSI) - perm = vdev->msi_perm; - - if (cap_id > PCI_CAP_ID_BASIC) - cap_start = vfio_find_cap_start(vdev, *ppos); - } - - WARN_ON(!cap_start && cap_id != PCI_CAP_ID_BASIC); - WARN_ON(cap_start > *ppos); - - offset = *ppos - cap_start; - - if (iswrite) { - if (!perm->writefn) - return ret; - - if (copy_from_user(&val, buf, count)) - return -EFAULT; - - ret = perm->writefn(vdev, *ppos, count, perm, offset, val); - } else { - if (perm->readfn) { - ret = perm->readfn(vdev, *ppos, count, - perm, offset, &val); - if (ret < 0) - return ret; - } - - if (copy_to_user(buf, &val, count)) - return -EFAULT; - } - - return ret; -} - -ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite) -{ - size_t done = 0; - int ret = 0; - loff_t pos = *ppos; - - pos &= VFIO_PCI_OFFSET_MASK; - - /* - * We want to both keep the access size the caller users as well as - * support reading large chunks of config space in a single call. - * PCI doesn't support unaligned accesses, so we can safely break - * those apart. - */ - while (count) { - if (count >= 4 && !(pos % 4)) - ret = vfio_config_do_rw(vdev, buf, 4, &pos, iswrite); - else if (count >= 2 && !(pos % 2)) - ret = vfio_config_do_rw(vdev, buf, 2, &pos, iswrite); - else - ret = vfio_config_do_rw(vdev, buf, 1, &pos, iswrite); - - if (ret < 0) - return ret; - - count -= ret; - done += ret; - buf += ret; - pos += ret; - } - - *ppos += done; - - return done; -} diff --git a/trunk/drivers/vfio/pci/vfio_pci_intrs.c b/trunk/drivers/vfio/pci/vfio_pci_intrs.c deleted file mode 100644 index 211a4920b88a..000000000000 --- a/trunk/drivers/vfio/pci/vfio_pci_intrs.c +++ /dev/null @@ -1,740 +0,0 @@ -/* - * VFIO PCI interrupt handling - * - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * Author: Alex Williamson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from original vfio: - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * Author: Tom Lyon, pugs@cisco.com - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vfio_pci_private.h" - -/* - * IRQfd - generic - */ -struct virqfd { - struct vfio_pci_device *vdev; - struct eventfd_ctx *eventfd; - int (*handler)(struct vfio_pci_device *, void *); - void (*thread)(struct vfio_pci_device *, void *); - void *data; - struct work_struct inject; - wait_queue_t wait; - poll_table pt; - struct work_struct shutdown; - struct virqfd **pvirqfd; -}; - -static struct workqueue_struct *vfio_irqfd_cleanup_wq; - -int __init vfio_pci_virqfd_init(void) -{ - vfio_irqfd_cleanup_wq = - create_singlethread_workqueue("vfio-irqfd-cleanup"); - if (!vfio_irqfd_cleanup_wq) - return -ENOMEM; - - return 0; -} - -void vfio_pci_virqfd_exit(void) -{ - destroy_workqueue(vfio_irqfd_cleanup_wq); -} - -static void virqfd_deactivate(struct virqfd *virqfd) -{ - queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); -} - -static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) -{ - struct virqfd *virqfd = container_of(wait, struct virqfd, wait); - unsigned long flags = (unsigned long)key; - - if (flags & POLLIN) { - /* An event has been signaled, call function */ - if ((!virqfd->handler || - virqfd->handler(virqfd->vdev, virqfd->data)) && - virqfd->thread) - schedule_work(&virqfd->inject); - } - - if (flags & POLLHUP) - /* The eventfd is closing, detach from VFIO */ - virqfd_deactivate(virqfd); - - return 0; -} - -static void virqfd_ptable_queue_proc(struct file *file, - wait_queue_head_t *wqh, poll_table *pt) -{ - struct virqfd *virqfd = container_of(pt, struct virqfd, pt); - add_wait_queue(wqh, &virqfd->wait); -} - -static void virqfd_shutdown(struct work_struct *work) -{ - struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); - struct virqfd **pvirqfd = virqfd->pvirqfd; - u64 cnt; - - eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); - flush_work(&virqfd->inject); - eventfd_ctx_put(virqfd->eventfd); - - kfree(virqfd); - *pvirqfd = NULL; -} - -static void virqfd_inject(struct work_struct *work) -{ - struct virqfd *virqfd = container_of(work, struct virqfd, inject); - if (virqfd->thread) - virqfd->thread(virqfd->vdev, virqfd->data); -} - -static int virqfd_enable(struct vfio_pci_device *vdev, - int (*handler)(struct vfio_pci_device *, void *), - void (*thread)(struct vfio_pci_device *, void *), - void *data, struct virqfd **pvirqfd, int fd) -{ - struct file *file = NULL; - struct eventfd_ctx *ctx = NULL; - struct virqfd *virqfd; - int ret = 0; - unsigned int events; - - if (*pvirqfd) - return -EBUSY; - - virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); - if (!virqfd) - return -ENOMEM; - - virqfd->pvirqfd = pvirqfd; - *pvirqfd = virqfd; - virqfd->vdev = vdev; - virqfd->handler = handler; - virqfd->thread = thread; - virqfd->data = data; - - INIT_WORK(&virqfd->shutdown, virqfd_shutdown); - INIT_WORK(&virqfd->inject, virqfd_inject); - - file = eventfd_fget(fd); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto fail; - } - - ctx = eventfd_ctx_fileget(file); - if (IS_ERR(ctx)) { - ret = PTR_ERR(ctx); - goto fail; - } - - virqfd->eventfd = ctx; - - /* - * Install our own custom wake-up handling so we are notified via - * a callback whenever someone signals the underlying eventfd. - */ - init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); - init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); - - events = file->f_op->poll(file, &virqfd->pt); - - /* - * Check if there was an event already pending on the eventfd - * before we registered and trigger it as if we didn't miss it. - */ - if (events & POLLIN) { - if ((!handler || handler(vdev, data)) && thread) - schedule_work(&virqfd->inject); - } - - /* - * Do not drop the file until the irqfd is fully initialized, - * otherwise we might race against the POLLHUP. - */ - fput(file); - - return 0; - -fail: - if (ctx && !IS_ERR(ctx)) - eventfd_ctx_put(ctx); - - if (file && !IS_ERR(file)) - fput(file); - - kfree(virqfd); - *pvirqfd = NULL; - - return ret; -} - -static void virqfd_disable(struct virqfd *virqfd) -{ - if (!virqfd) - return; - - virqfd_deactivate(virqfd); - - /* Block until we know all outstanding shutdown jobs have completed. */ - flush_workqueue(vfio_irqfd_cleanup_wq); -} - -/* - * INTx - */ -static void vfio_send_intx_eventfd(struct vfio_pci_device *vdev, void *unused) -{ - if (likely(is_intx(vdev) && !vdev->virq_disabled)) - eventfd_signal(vdev->ctx[0].trigger, 1); -} - -void vfio_pci_intx_mask(struct vfio_pci_device *vdev) -{ - struct pci_dev *pdev = vdev->pdev; - unsigned long flags; - - spin_lock_irqsave(&vdev->irqlock, flags); - - /* - * Masking can come from interrupt, ioctl, or config space - * via INTx disable. The latter means this can get called - * even when not using intx delivery. In this case, just - * try to have the physical bit follow the virtual bit. - */ - if (unlikely(!is_intx(vdev))) { - if (vdev->pci_2_3) - pci_intx(pdev, 0); - } else if (!vdev->ctx[0].masked) { - /* - * Can't use check_and_mask here because we always want to - * mask, not just when something is pending. - */ - if (vdev->pci_2_3) - pci_intx(pdev, 0); - else - disable_irq_nosync(pdev->irq); - - vdev->ctx[0].masked = true; - } - - spin_unlock_irqrestore(&vdev->irqlock, flags); -} - -/* - * If this is triggered by an eventfd, we can't call eventfd_signal - * or else we'll deadlock on the eventfd wait queue. Return >0 when - * a signal is necessary, which can then be handled via a work queue - * or directly depending on the caller. - */ -int vfio_pci_intx_unmask_handler(struct vfio_pci_device *vdev, void *unused) -{ - struct pci_dev *pdev = vdev->pdev; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&vdev->irqlock, flags); - - /* - * Unmasking comes from ioctl or config, so again, have the - * physical bit follow the virtual even when not using INTx. - */ - if (unlikely(!is_intx(vdev))) { - if (vdev->pci_2_3) - pci_intx(pdev, 1); - } else if (vdev->ctx[0].masked && !vdev->virq_disabled) { - /* - * A pending interrupt here would immediately trigger, - * but we can avoid that overhead by just re-sending - * the interrupt to the user. - */ - if (vdev->pci_2_3) { - if (!pci_check_and_unmask_intx(pdev)) - ret = 1; - } else - enable_irq(pdev->irq); - - vdev->ctx[0].masked = (ret > 0); - } - - spin_unlock_irqrestore(&vdev->irqlock, flags); - - return ret; -} - -void vfio_pci_intx_unmask(struct vfio_pci_device *vdev) -{ - if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0) - vfio_send_intx_eventfd(vdev, NULL); -} - -static irqreturn_t vfio_intx_handler(int irq, void *dev_id) -{ - struct vfio_pci_device *vdev = dev_id; - unsigned long flags; - int ret = IRQ_NONE; - - spin_lock_irqsave(&vdev->irqlock, flags); - - if (!vdev->pci_2_3) { - disable_irq_nosync(vdev->pdev->irq); - vdev->ctx[0].masked = true; - ret = IRQ_HANDLED; - } else if (!vdev->ctx[0].masked && /* may be shared */ - pci_check_and_mask_intx(vdev->pdev)) { - vdev->ctx[0].masked = true; - ret = IRQ_HANDLED; - } - - spin_unlock_irqrestore(&vdev->irqlock, flags); - - if (ret == IRQ_HANDLED) - vfio_send_intx_eventfd(vdev, NULL); - - return ret; -} - -static int vfio_intx_enable(struct vfio_pci_device *vdev) -{ - if (!is_irq_none(vdev)) - return -EINVAL; - - if (!vdev->pdev->irq) - return -ENODEV; - - vdev->ctx = kzalloc(sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); - if (!vdev->ctx) - return -ENOMEM; - - vdev->num_ctx = 1; - vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX; - - return 0; -} - -static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd) -{ - struct pci_dev *pdev = vdev->pdev; - unsigned long irqflags = IRQF_SHARED; - struct eventfd_ctx *trigger; - unsigned long flags; - int ret; - - if (vdev->ctx[0].trigger) { - free_irq(pdev->irq, vdev); - kfree(vdev->ctx[0].name); - eventfd_ctx_put(vdev->ctx[0].trigger); - vdev->ctx[0].trigger = NULL; - } - - if (fd < 0) /* Disable only */ - return 0; - - vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)", - pci_name(pdev)); - if (!vdev->ctx[0].name) - return -ENOMEM; - - trigger = eventfd_ctx_fdget(fd); - if (IS_ERR(trigger)) { - kfree(vdev->ctx[0].name); - return PTR_ERR(trigger); - } - - if (!vdev->pci_2_3) - irqflags = 0; - - ret = request_irq(pdev->irq, vfio_intx_handler, - irqflags, vdev->ctx[0].name, vdev); - if (ret) { - kfree(vdev->ctx[0].name); - eventfd_ctx_put(trigger); - return ret; - } - - vdev->ctx[0].trigger = trigger; - - /* - * INTx disable will stick across the new irq setup, - * disable_irq won't. - */ - spin_lock_irqsave(&vdev->irqlock, flags); - if (!vdev->pci_2_3 && (vdev->ctx[0].masked || vdev->virq_disabled)) - disable_irq_nosync(pdev->irq); - spin_unlock_irqrestore(&vdev->irqlock, flags); - - return 0; -} - -static void vfio_intx_disable(struct vfio_pci_device *vdev) -{ - vfio_intx_set_signal(vdev, -1); - virqfd_disable(vdev->ctx[0].unmask); - virqfd_disable(vdev->ctx[0].mask); - vdev->irq_type = VFIO_PCI_NUM_IRQS; - vdev->num_ctx = 0; - kfree(vdev->ctx); -} - -/* - * MSI/MSI-X - */ -static irqreturn_t vfio_msihandler(int irq, void *arg) -{ - struct eventfd_ctx *trigger = arg; - - eventfd_signal(trigger, 1); - return IRQ_HANDLED; -} - -static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) -{ - struct pci_dev *pdev = vdev->pdev; - int ret; - - if (!is_irq_none(vdev)) - return -EINVAL; - - vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); - if (!vdev->ctx) - return -ENOMEM; - - if (msix) { - int i; - - vdev->msix = kzalloc(nvec * sizeof(struct msix_entry), - GFP_KERNEL); - if (!vdev->msix) { - kfree(vdev->ctx); - return -ENOMEM; - } - - for (i = 0; i < nvec; i++) - vdev->msix[i].entry = i; - - ret = pci_enable_msix(pdev, vdev->msix, nvec); - if (ret) { - kfree(vdev->msix); - kfree(vdev->ctx); - return ret; - } - } else { - ret = pci_enable_msi_block(pdev, nvec); - if (ret) { - kfree(vdev->ctx); - return ret; - } - } - - vdev->num_ctx = nvec; - vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX : - VFIO_PCI_MSI_IRQ_INDEX; - - if (!msix) { - /* - * Compute the virtual hardware field for max msi vectors - - * it is the log base 2 of the number of vectors. - */ - vdev->msi_qmax = fls(nvec * 2 - 1) - 1; - } - - return 0; -} - -static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, - int vector, int fd, bool msix) -{ - struct pci_dev *pdev = vdev->pdev; - int irq = msix ? vdev->msix[vector].vector : pdev->irq + vector; - char *name = msix ? "vfio-msix" : "vfio-msi"; - struct eventfd_ctx *trigger; - int ret; - - if (vector >= vdev->num_ctx) - return -EINVAL; - - if (vdev->ctx[vector].trigger) { - free_irq(irq, vdev->ctx[vector].trigger); - kfree(vdev->ctx[vector].name); - eventfd_ctx_put(vdev->ctx[vector].trigger); - vdev->ctx[vector].trigger = NULL; - } - - if (fd < 0) - return 0; - - vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "%s[%d](%s)", - name, vector, pci_name(pdev)); - if (!vdev->ctx[vector].name) - return -ENOMEM; - - trigger = eventfd_ctx_fdget(fd); - if (IS_ERR(trigger)) { - kfree(vdev->ctx[vector].name); - return PTR_ERR(trigger); - } - - ret = request_irq(irq, vfio_msihandler, 0, - vdev->ctx[vector].name, trigger); - if (ret) { - kfree(vdev->ctx[vector].name); - eventfd_ctx_put(trigger); - return ret; - } - - vdev->ctx[vector].trigger = trigger; - - return 0; -} - -static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start, - unsigned count, int32_t *fds, bool msix) -{ - int i, j, ret = 0; - - if (start + count > vdev->num_ctx) - return -EINVAL; - - for (i = 0, j = start; i < count && !ret; i++, j++) { - int fd = fds ? fds[i] : -1; - ret = vfio_msi_set_vector_signal(vdev, j, fd, msix); - } - - if (ret) { - for (--j; j >= start; j--) - vfio_msi_set_vector_signal(vdev, j, -1, msix); - } - - return ret; -} - -static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix) -{ - struct pci_dev *pdev = vdev->pdev; - int i; - - vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix); - - for (i = 0; i < vdev->num_ctx; i++) { - virqfd_disable(vdev->ctx[i].unmask); - virqfd_disable(vdev->ctx[i].mask); - } - - if (msix) { - pci_disable_msix(vdev->pdev); - kfree(vdev->msix); - } else - pci_disable_msi(pdev); - - vdev->irq_type = VFIO_PCI_NUM_IRQS; - vdev->num_ctx = 0; - kfree(vdev->ctx); -} - -/* - * IOCTL support - */ -static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev, - unsigned index, unsigned start, - unsigned count, uint32_t flags, void *data) -{ - if (!is_intx(vdev) || start != 0 || count != 1) - return -EINVAL; - - if (flags & VFIO_IRQ_SET_DATA_NONE) { - vfio_pci_intx_unmask(vdev); - } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { - uint8_t unmask = *(uint8_t *)data; - if (unmask) - vfio_pci_intx_unmask(vdev); - } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { - int32_t fd = *(int32_t *)data; - if (fd >= 0) - return virqfd_enable(vdev, vfio_pci_intx_unmask_handler, - vfio_send_intx_eventfd, NULL, - &vdev->ctx[0].unmask, fd); - - virqfd_disable(vdev->ctx[0].unmask); - } - - return 0; -} - -static int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev, - unsigned index, unsigned start, - unsigned count, uint32_t flags, void *data) -{ - if (!is_intx(vdev) || start != 0 || count != 1) - return -EINVAL; - - if (flags & VFIO_IRQ_SET_DATA_NONE) { - vfio_pci_intx_mask(vdev); - } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { - uint8_t mask = *(uint8_t *)data; - if (mask) - vfio_pci_intx_mask(vdev); - } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { - return -ENOTTY; /* XXX implement me */ - } - - return 0; -} - -static int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev, - unsigned index, unsigned start, - unsigned count, uint32_t flags, void *data) -{ - if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { - vfio_intx_disable(vdev); - return 0; - } - - if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1) - return -EINVAL; - - if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { - int32_t fd = *(int32_t *)data; - int ret; - - if (is_intx(vdev)) - return vfio_intx_set_signal(vdev, fd); - - ret = vfio_intx_enable(vdev); - if (ret) - return ret; - - ret = vfio_intx_set_signal(vdev, fd); - if (ret) - vfio_intx_disable(vdev); - - return ret; - } - - if (!is_intx(vdev)) - return -EINVAL; - - if (flags & VFIO_IRQ_SET_DATA_NONE) { - vfio_send_intx_eventfd(vdev, NULL); - } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { - uint8_t trigger = *(uint8_t *)data; - if (trigger) - vfio_send_intx_eventfd(vdev, NULL); - } - return 0; -} - -static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev, - unsigned index, unsigned start, - unsigned count, uint32_t flags, void *data) -{ - int i; - bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false; - - if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { - vfio_msi_disable(vdev, msix); - return 0; - } - - if (!(irq_is(vdev, index) || is_irq_none(vdev))) - return -EINVAL; - - if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { - int32_t *fds = data; - int ret; - - if (vdev->irq_type == index) - return vfio_msi_set_block(vdev, start, count, - fds, msix); - - ret = vfio_msi_enable(vdev, start + count, msix); - if (ret) - return ret; - - ret = vfio_msi_set_block(vdev, start, count, fds, msix); - if (ret) - vfio_msi_disable(vdev, msix); - - return ret; - } - - if (!irq_is(vdev, index) || start + count > vdev->num_ctx) - return -EINVAL; - - for (i = start; i < start + count; i++) { - if (!vdev->ctx[i].trigger) - continue; - if (flags & VFIO_IRQ_SET_DATA_NONE) { - eventfd_signal(vdev->ctx[i].trigger, 1); - } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { - uint8_t *bools = data; - if (bools[i - start]) - eventfd_signal(vdev->ctx[i].trigger, 1); - } - } - return 0; -} - -int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, - unsigned index, unsigned start, unsigned count, - void *data) -{ - int (*func)(struct vfio_pci_device *vdev, unsigned index, - unsigned start, unsigned count, uint32_t flags, - void *data) = NULL; - - switch (index) { - case VFIO_PCI_INTX_IRQ_INDEX: - switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { - case VFIO_IRQ_SET_ACTION_MASK: - func = vfio_pci_set_intx_mask; - break; - case VFIO_IRQ_SET_ACTION_UNMASK: - func = vfio_pci_set_intx_unmask; - break; - case VFIO_IRQ_SET_ACTION_TRIGGER: - func = vfio_pci_set_intx_trigger; - break; - } - break; - case VFIO_PCI_MSI_IRQ_INDEX: - case VFIO_PCI_MSIX_IRQ_INDEX: - switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { - case VFIO_IRQ_SET_ACTION_MASK: - case VFIO_IRQ_SET_ACTION_UNMASK: - /* XXX Need masking support exported */ - break; - case VFIO_IRQ_SET_ACTION_TRIGGER: - func = vfio_pci_set_msi_trigger; - break; - } - break; - } - - if (!func) - return -ENOTTY; - - return func(vdev, index, start, count, flags, data); -} diff --git a/trunk/drivers/vfio/pci/vfio_pci_private.h b/trunk/drivers/vfio/pci/vfio_pci_private.h deleted file mode 100644 index 611827cba8cd..000000000000 --- a/trunk/drivers/vfio/pci/vfio_pci_private.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * Author: Alex Williamson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from original vfio: - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * Author: Tom Lyon, pugs@cisco.com - */ - -#include -#include - -#ifndef VFIO_PCI_PRIVATE_H -#define VFIO_PCI_PRIVATE_H - -#define VFIO_PCI_OFFSET_SHIFT 40 - -#define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT) -#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT) -#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1) - -struct vfio_pci_irq_ctx { - struct eventfd_ctx *trigger; - struct virqfd *unmask; - struct virqfd *mask; - char *name; - bool masked; -}; - -struct vfio_pci_device { - struct pci_dev *pdev; - void __iomem *barmap[PCI_STD_RESOURCE_END + 1]; - u8 *pci_config_map; - u8 *vconfig; - struct perm_bits *msi_perm; - spinlock_t irqlock; - struct mutex igate; - struct msix_entry *msix; - struct vfio_pci_irq_ctx *ctx; - int num_ctx; - int irq_type; - u8 msi_qmax; - u8 msix_bar; - u16 msix_size; - u32 msix_offset; - u32 rbar[7]; - bool pci_2_3; - bool virq_disabled; - bool reset_works; - bool extended_caps; - bool bardirty; - struct pci_saved_state *pci_saved_state; - atomic_t refcnt; -}; - -#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) -#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX) -#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX) -#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev))) -#define irq_is(vdev, type) (vdev->irq_type == type) - -extern void vfio_pci_intx_mask(struct vfio_pci_device *vdev); -extern void vfio_pci_intx_unmask(struct vfio_pci_device *vdev); - -extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, - uint32_t flags, unsigned index, - unsigned start, unsigned count, void *data); - -extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); -extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); -extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); - -extern int vfio_pci_init_perm_bits(void); -extern void vfio_pci_uninit_perm_bits(void); - -extern int vfio_pci_virqfd_init(void); -extern void vfio_pci_virqfd_exit(void); - -extern int vfio_config_init(struct vfio_pci_device *vdev); -extern void vfio_config_free(struct vfio_pci_device *vdev); -#endif /* VFIO_PCI_PRIVATE_H */ diff --git a/trunk/drivers/vfio/pci/vfio_pci_rdwr.c b/trunk/drivers/vfio/pci/vfio_pci_rdwr.c deleted file mode 100644 index 4362d9e7baa3..000000000000 --- a/trunk/drivers/vfio/pci/vfio_pci_rdwr.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * VFIO PCI I/O Port & MMIO access - * - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * Author: Alex Williamson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from original vfio: - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * Author: Tom Lyon, pugs@cisco.com - */ - -#include -#include -#include -#include - -#include "vfio_pci_private.h" - -/* I/O Port BAR access */ -ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf, - size_t count, loff_t *ppos, bool iswrite) -{ - struct pci_dev *pdev = vdev->pdev; - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - void __iomem *io; - size_t done = 0; - - if (!pci_resource_start(pdev, bar)) - return -EINVAL; - - if (pos + count > pci_resource_len(pdev, bar)) - return -EINVAL; - - if (!vdev->barmap[bar]) { - int ret; - - ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); - if (ret) - return ret; - - vdev->barmap[bar] = pci_iomap(pdev, bar, 0); - - if (!vdev->barmap[bar]) { - pci_release_selected_regions(pdev, 1 << bar); - return -EINVAL; - } - } - - io = vdev->barmap[bar]; - - while (count) { - int filled; - - if (count >= 3 && !(pos % 4)) { - __le32 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 4)) - return -EFAULT; - - iowrite32(le32_to_cpu(val), io + pos); - } else { - val = cpu_to_le32(ioread32(io + pos)); - - if (copy_to_user(buf, &val, 4)) - return -EFAULT; - } - - filled = 4; - - } else if ((pos % 2) == 0 && count >= 2) { - __le16 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 2)) - return -EFAULT; - - iowrite16(le16_to_cpu(val), io + pos); - } else { - val = cpu_to_le16(ioread16(io + pos)); - - if (copy_to_user(buf, &val, 2)) - return -EFAULT; - } - - filled = 2; - } else { - u8 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 1)) - return -EFAULT; - - iowrite8(val, io + pos); - } else { - val = ioread8(io + pos); - - if (copy_to_user(buf, &val, 1)) - return -EFAULT; - } - - filled = 1; - } - - count -= filled; - done += filled; - buf += filled; - pos += filled; - } - - *ppos += done; - - return done; -} - -/* - * MMIO BAR access - * We handle two excluded ranges here as well, if the user tries to read - * the ROM beyond what PCI tells us is available or the MSI-X table region, - * we return 0xFF and writes are dropped. - */ -ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, - size_t count, loff_t *ppos, bool iswrite) -{ - struct pci_dev *pdev = vdev->pdev; - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - void __iomem *io; - resource_size_t end; - size_t done = 0; - size_t x_start = 0, x_end = 0; /* excluded range */ - - if (!pci_resource_start(pdev, bar)) - return -EINVAL; - - end = pci_resource_len(pdev, bar); - - if (pos > end) - return -EINVAL; - - if (pos == end) - return 0; - - if (pos + count > end) - count = end - pos; - - if (bar == PCI_ROM_RESOURCE) { - io = pci_map_rom(pdev, &x_start); - x_end = end; - } else { - if (!vdev->barmap[bar]) { - int ret; - - ret = pci_request_selected_regions(pdev, 1 << bar, - "vfio"); - if (ret) - return ret; - - vdev->barmap[bar] = pci_iomap(pdev, bar, 0); - - if (!vdev->barmap[bar]) { - pci_release_selected_regions(pdev, 1 << bar); - return -EINVAL; - } - } - - io = vdev->barmap[bar]; - - if (bar == vdev->msix_bar) { - x_start = vdev->msix_offset; - x_end = vdev->msix_offset + vdev->msix_size; - } - } - - if (!io) - return -EINVAL; - - while (count) { - size_t fillable, filled; - - if (pos < x_start) - fillable = x_start - pos; - else if (pos >= x_end) - fillable = end - pos; - else - fillable = 0; - - if (fillable >= 4 && !(pos % 4) && (count >= 4)) { - __le32 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 4)) - goto out; - - iowrite32(le32_to_cpu(val), io + pos); - } else { - val = cpu_to_le32(ioread32(io + pos)); - - if (copy_to_user(buf, &val, 4)) - goto out; - } - - filled = 4; - } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) { - __le16 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 2)) - goto out; - - iowrite16(le16_to_cpu(val), io + pos); - } else { - val = cpu_to_le16(ioread16(io + pos)); - - if (copy_to_user(buf, &val, 2)) - goto out; - } - - filled = 2; - } else if (fillable) { - u8 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 1)) - goto out; - - iowrite8(val, io + pos); - } else { - val = ioread8(io + pos); - - if (copy_to_user(buf, &val, 1)) - goto out; - } - - filled = 1; - } else { - /* Drop writes, fill reads with FF */ - if (!iswrite) { - char val = 0xFF; - size_t i; - - for (i = 0; i < x_end - pos; i++) { - if (put_user(val, buf + i)) - goto out; - } - } - - filled = x_end - pos; - } - - count -= filled; - done += filled; - buf += filled; - pos += filled; - } - - *ppos += done; - -out: - if (bar == PCI_ROM_RESOURCE) - pci_unmap_rom(pdev, io); - - return count ? -EFAULT : done; -} diff --git a/trunk/drivers/vfio/vfio.c b/trunk/drivers/vfio/vfio.c deleted file mode 100644 index 9591e2b509d7..000000000000 --- a/trunk/drivers/vfio/vfio.c +++ /dev/null @@ -1,1420 +0,0 @@ -/* - * VFIO core - * - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * Author: Alex Williamson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from original vfio: - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * Author: Tom Lyon, pugs@cisco.com - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_VERSION "0.3" -#define DRIVER_AUTHOR "Alex Williamson " -#define DRIVER_DESC "VFIO - User Level meta-driver" - -static struct vfio { - struct class *class; - struct list_head iommu_drivers_list; - struct mutex iommu_drivers_lock; - struct list_head group_list; - struct idr group_idr; - struct mutex group_lock; - struct cdev group_cdev; - struct device *dev; - dev_t devt; - struct cdev cdev; - wait_queue_head_t release_q; -} vfio; - -struct vfio_iommu_driver { - const struct vfio_iommu_driver_ops *ops; - struct list_head vfio_next; -}; - -struct vfio_container { - struct kref kref; - struct list_head group_list; - struct mutex group_lock; - struct vfio_iommu_driver *iommu_driver; - void *iommu_data; -}; - -struct vfio_group { - struct kref kref; - int minor; - atomic_t container_users; - struct iommu_group *iommu_group; - struct vfio_container *container; - struct list_head device_list; - struct mutex device_lock; - struct device *dev; - struct notifier_block nb; - struct list_head vfio_next; - struct list_head container_next; -}; - -struct vfio_device { - struct kref kref; - struct device *dev; - const struct vfio_device_ops *ops; - struct vfio_group *group; - struct list_head group_next; - void *device_data; -}; - -/** - * IOMMU driver registration - */ -int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops) -{ - struct vfio_iommu_driver *driver, *tmp; - - driver = kzalloc(sizeof(*driver), GFP_KERNEL); - if (!driver) - return -ENOMEM; - - driver->ops = ops; - - mutex_lock(&vfio.iommu_drivers_lock); - - /* Check for duplicates */ - list_for_each_entry(tmp, &vfio.iommu_drivers_list, vfio_next) { - if (tmp->ops == ops) { - mutex_unlock(&vfio.iommu_drivers_lock); - kfree(driver); - return -EINVAL; - } - } - - list_add(&driver->vfio_next, &vfio.iommu_drivers_list); - - mutex_unlock(&vfio.iommu_drivers_lock); - - return 0; -} -EXPORT_SYMBOL_GPL(vfio_register_iommu_driver); - -void vfio_unregister_iommu_driver(const struct vfio_iommu_driver_ops *ops) -{ - struct vfio_iommu_driver *driver; - - mutex_lock(&vfio.iommu_drivers_lock); - list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) { - if (driver->ops == ops) { - list_del(&driver->vfio_next); - mutex_unlock(&vfio.iommu_drivers_lock); - kfree(driver); - return; - } - } - mutex_unlock(&vfio.iommu_drivers_lock); -} -EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver); - -/** - * Group minor allocation/free - both called with vfio.group_lock held - */ -static int vfio_alloc_group_minor(struct vfio_group *group) -{ - int ret, minor; - -again: - if (unlikely(idr_pre_get(&vfio.group_idr, GFP_KERNEL) == 0)) - return -ENOMEM; - - /* index 0 is used by /dev/vfio/vfio */ - ret = idr_get_new_above(&vfio.group_idr, group, 1, &minor); - if (ret == -EAGAIN) - goto again; - if (ret || minor > MINORMASK) { - if (minor > MINORMASK) - idr_remove(&vfio.group_idr, minor); - return -ENOSPC; - } - - return minor; -} - -static void vfio_free_group_minor(int minor) -{ - idr_remove(&vfio.group_idr, minor); -} - -static int vfio_iommu_group_notifier(struct notifier_block *nb, - unsigned long action, void *data); -static void vfio_group_get(struct vfio_group *group); - -/** - * Container objects - containers are created when /dev/vfio/vfio is - * opened, but their lifecycle extends until the last user is done, so - * it's freed via kref. Must support container/group/device being - * closed in any order. - */ -static void vfio_container_get(struct vfio_container *container) -{ - kref_get(&container->kref); -} - -static void vfio_container_release(struct kref *kref) -{ - struct vfio_container *container; - container = container_of(kref, struct vfio_container, kref); - - kfree(container); -} - -static void vfio_container_put(struct vfio_container *container) -{ - kref_put(&container->kref, vfio_container_release); -} - -/** - * Group objects - create, release, get, put, search - */ -static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) -{ - struct vfio_group *group, *tmp; - struct device *dev; - int ret, minor; - - group = kzalloc(sizeof(*group), GFP_KERNEL); - if (!group) - return ERR_PTR(-ENOMEM); - - kref_init(&group->kref); - INIT_LIST_HEAD(&group->device_list); - mutex_init(&group->device_lock); - atomic_set(&group->container_users, 0); - group->iommu_group = iommu_group; - - group->nb.notifier_call = vfio_iommu_group_notifier; - - /* - * blocking notifiers acquire a rwsem around registering and hold - * it around callback. Therefore, need to register outside of - * vfio.group_lock to avoid A-B/B-A contention. Our callback won't - * do anything unless it can find the group in vfio.group_list, so - * no harm in registering early. - */ - ret = iommu_group_register_notifier(iommu_group, &group->nb); - if (ret) { - kfree(group); - return ERR_PTR(ret); - } - - mutex_lock(&vfio.group_lock); - - minor = vfio_alloc_group_minor(group); - if (minor < 0) { - mutex_unlock(&vfio.group_lock); - kfree(group); - return ERR_PTR(minor); - } - - /* Did we race creating this group? */ - list_for_each_entry(tmp, &vfio.group_list, vfio_next) { - if (tmp->iommu_group == iommu_group) { - vfio_group_get(tmp); - vfio_free_group_minor(minor); - mutex_unlock(&vfio.group_lock); - kfree(group); - return tmp; - } - } - - dev = device_create(vfio.class, NULL, MKDEV(MAJOR(vfio.devt), minor), - group, "%d", iommu_group_id(iommu_group)); - if (IS_ERR(dev)) { - vfio_free_group_minor(minor); - mutex_unlock(&vfio.group_lock); - kfree(group); - return (struct vfio_group *)dev; /* ERR_PTR */ - } - - group->minor = minor; - group->dev = dev; - - list_add(&group->vfio_next, &vfio.group_list); - - mutex_unlock(&vfio.group_lock); - - return group; -} - -static void vfio_group_release(struct kref *kref) -{ - struct vfio_group *group = container_of(kref, struct vfio_group, kref); - - WARN_ON(!list_empty(&group->device_list)); - - device_destroy(vfio.class, MKDEV(MAJOR(vfio.devt), group->minor)); - list_del(&group->vfio_next); - vfio_free_group_minor(group->minor); - - mutex_unlock(&vfio.group_lock); - - /* - * Unregister outside of lock. A spurious callback is harmless now - * that the group is no longer in vfio.group_list. - */ - iommu_group_unregister_notifier(group->iommu_group, &group->nb); - - kfree(group); -} - -static void vfio_group_put(struct vfio_group *group) -{ - mutex_lock(&vfio.group_lock); - /* - * Release needs to unlock to unregister the notifier, so only - * unlock if not released. - */ - if (!kref_put(&group->kref, vfio_group_release)) - mutex_unlock(&vfio.group_lock); -} - -/* Assume group_lock or group reference is held */ -static void vfio_group_get(struct vfio_group *group) -{ - kref_get(&group->kref); -} - -/* - * Not really a try as we will sleep for mutex, but we need to make - * sure the group pointer is valid under lock and get a reference. - */ -static struct vfio_group *vfio_group_try_get(struct vfio_group *group) -{ - struct vfio_group *target = group; - - mutex_lock(&vfio.group_lock); - list_for_each_entry(group, &vfio.group_list, vfio_next) { - if (group == target) { - vfio_group_get(group); - mutex_unlock(&vfio.group_lock); - return group; - } - } - mutex_unlock(&vfio.group_lock); - - return NULL; -} - -static -struct vfio_group *vfio_group_get_from_iommu(struct iommu_group *iommu_group) -{ - struct vfio_group *group; - - mutex_lock(&vfio.group_lock); - list_for_each_entry(group, &vfio.group_list, vfio_next) { - if (group->iommu_group == iommu_group) { - vfio_group_get(group); - mutex_unlock(&vfio.group_lock); - return group; - } - } - mutex_unlock(&vfio.group_lock); - - return NULL; -} - -static struct vfio_group *vfio_group_get_from_minor(int minor) -{ - struct vfio_group *group; - - mutex_lock(&vfio.group_lock); - group = idr_find(&vfio.group_idr, minor); - if (!group) { - mutex_unlock(&vfio.group_lock); - return NULL; - } - vfio_group_get(group); - mutex_unlock(&vfio.group_lock); - - return group; -} - -/** - * Device objects - create, release, get, put, search - */ -static -struct vfio_device *vfio_group_create_device(struct vfio_group *group, - struct device *dev, - const struct vfio_device_ops *ops, - void *device_data) -{ - struct vfio_device *device; - int ret; - - device = kzalloc(sizeof(*device), GFP_KERNEL); - if (!device) - return ERR_PTR(-ENOMEM); - - kref_init(&device->kref); - device->dev = dev; - device->group = group; - device->ops = ops; - device->device_data = device_data; - - ret = dev_set_drvdata(dev, device); - if (ret) { - kfree(device); - return ERR_PTR(ret); - } - - /* No need to get group_lock, caller has group reference */ - vfio_group_get(group); - - mutex_lock(&group->device_lock); - list_add(&device->group_next, &group->device_list); - mutex_unlock(&group->device_lock); - - return device; -} - -static void vfio_device_release(struct kref *kref) -{ - struct vfio_device *device = container_of(kref, - struct vfio_device, kref); - struct vfio_group *group = device->group; - - mutex_lock(&group->device_lock); - list_del(&device->group_next); - mutex_unlock(&group->device_lock); - - dev_set_drvdata(device->dev, NULL); - - kfree(device); - - /* vfio_del_group_dev may be waiting for this device */ - wake_up(&vfio.release_q); -} - -/* Device reference always implies a group reference */ -static void vfio_device_put(struct vfio_device *device) -{ - kref_put(&device->kref, vfio_device_release); - vfio_group_put(device->group); -} - -static void vfio_device_get(struct vfio_device *device) -{ - vfio_group_get(device->group); - kref_get(&device->kref); -} - -static struct vfio_device *vfio_group_get_device(struct vfio_group *group, - struct device *dev) -{ - struct vfio_device *device; - - mutex_lock(&group->device_lock); - list_for_each_entry(device, &group->device_list, group_next) { - if (device->dev == dev) { - vfio_device_get(device); - mutex_unlock(&group->device_lock); - return device; - } - } - mutex_unlock(&group->device_lock); - return NULL; -} - -/* - * Whitelist some drivers that we know are safe (no dma) or just sit on - * a device. It's not always practical to leave a device within a group - * driverless as it could get re-bound to something unsafe. - */ -static const char * const vfio_driver_whitelist[] = { "pci-stub" }; - -static bool vfio_whitelisted_driver(struct device_driver *drv) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(vfio_driver_whitelist); i++) { - if (!strcmp(drv->name, vfio_driver_whitelist[i])) - return true; - } - - return false; -} - -/* - * A vfio group is viable for use by userspace if all devices are either - * driver-less or bound to a vfio or whitelisted driver. We test the - * latter by the existence of a struct vfio_device matching the dev. - */ -static int vfio_dev_viable(struct device *dev, void *data) -{ - struct vfio_group *group = data; - struct vfio_device *device; - - if (!dev->driver || vfio_whitelisted_driver(dev->driver)) - return 0; - - device = vfio_group_get_device(group, dev); - if (device) { - vfio_device_put(device); - return 0; - } - - return -EINVAL; -} - -/** - * Async device support - */ -static int vfio_group_nb_add_dev(struct vfio_group *group, struct device *dev) -{ - struct vfio_device *device; - - /* Do we already know about it? We shouldn't */ - device = vfio_group_get_device(group, dev); - if (WARN_ON_ONCE(device)) { - vfio_device_put(device); - return 0; - } - - /* Nothing to do for idle groups */ - if (!atomic_read(&group->container_users)) - return 0; - - /* TODO Prevent device auto probing */ - WARN("Device %s added to live group %d!\n", dev_name(dev), - iommu_group_id(group->iommu_group)); - - return 0; -} - -static int vfio_group_nb_del_dev(struct vfio_group *group, struct device *dev) -{ - struct vfio_device *device; - - /* - * Expect to fall out here. If a device was in use, it would - * have been bound to a vfio sub-driver, which would have blocked - * in .remove at vfio_del_group_dev. Sanity check that we no - * longer track the device, so it's safe to remove. - */ - device = vfio_group_get_device(group, dev); - if (likely(!device)) - return 0; - - WARN("Device %s removed from live group %d!\n", dev_name(dev), - iommu_group_id(group->iommu_group)); - - vfio_device_put(device); - return 0; -} - -static int vfio_group_nb_verify(struct vfio_group *group, struct device *dev) -{ - /* We don't care what happens when the group isn't in use */ - if (!atomic_read(&group->container_users)) - return 0; - - return vfio_dev_viable(dev, group); -} - -static int vfio_iommu_group_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct vfio_group *group = container_of(nb, struct vfio_group, nb); - struct device *dev = data; - - /* - * Need to go through a group_lock lookup to get a reference or - * we risk racing a group being removed. Leave a WARN_ON for - * debuging, but if the group no longer exists, a spurious notify - * is harmless. - */ - group = vfio_group_try_get(group); - if (WARN_ON(!group)) - return NOTIFY_OK; - - switch (action) { - case IOMMU_GROUP_NOTIFY_ADD_DEVICE: - vfio_group_nb_add_dev(group, dev); - break; - case IOMMU_GROUP_NOTIFY_DEL_DEVICE: - vfio_group_nb_del_dev(group, dev); - break; - case IOMMU_GROUP_NOTIFY_BIND_DRIVER: - pr_debug("%s: Device %s, group %d binding to driver\n", - __func__, dev_name(dev), - iommu_group_id(group->iommu_group)); - break; - case IOMMU_GROUP_NOTIFY_BOUND_DRIVER: - pr_debug("%s: Device %s, group %d bound to driver %s\n", - __func__, dev_name(dev), - iommu_group_id(group->iommu_group), dev->driver->name); - BUG_ON(vfio_group_nb_verify(group, dev)); - break; - case IOMMU_GROUP_NOTIFY_UNBIND_DRIVER: - pr_debug("%s: Device %s, group %d unbinding from driver %s\n", - __func__, dev_name(dev), - iommu_group_id(group->iommu_group), dev->driver->name); - break; - case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER: - pr_debug("%s: Device %s, group %d unbound from driver\n", - __func__, dev_name(dev), - iommu_group_id(group->iommu_group)); - /* - * XXX An unbound device in a live group is ok, but we'd - * really like to avoid the above BUG_ON by preventing other - * drivers from binding to it. Once that occurs, we have to - * stop the system to maintain isolation. At a minimum, we'd - * want a toggle to disable driver auto probe for this device. - */ - break; - } - - vfio_group_put(group); - return NOTIFY_OK; -} - -/** - * VFIO driver API - */ -int vfio_add_group_dev(struct device *dev, - const struct vfio_device_ops *ops, void *device_data) -{ - struct iommu_group *iommu_group; - struct vfio_group *group; - struct vfio_device *device; - - iommu_group = iommu_group_get(dev); - if (!iommu_group) - return -EINVAL; - - group = vfio_group_get_from_iommu(iommu_group); - if (!group) { - group = vfio_create_group(iommu_group); - if (IS_ERR(group)) { - iommu_group_put(iommu_group); - return PTR_ERR(group); - } - } - - device = vfio_group_get_device(group, dev); - if (device) { - WARN(1, "Device %s already exists on group %d\n", - dev_name(dev), iommu_group_id(iommu_group)); - vfio_device_put(device); - vfio_group_put(group); - iommu_group_put(iommu_group); - return -EBUSY; - } - - device = vfio_group_create_device(group, dev, ops, device_data); - if (IS_ERR(device)) { - vfio_group_put(group); - iommu_group_put(iommu_group); - return PTR_ERR(device); - } - - /* - * Added device holds reference to iommu_group and vfio_device - * (which in turn holds reference to vfio_group). Drop extra - * group reference used while acquiring device. - */ - vfio_group_put(group); - - return 0; -} -EXPORT_SYMBOL_GPL(vfio_add_group_dev); - -/* Test whether a struct device is present in our tracking */ -static bool vfio_dev_present(struct device *dev) -{ - struct iommu_group *iommu_group; - struct vfio_group *group; - struct vfio_device *device; - - iommu_group = iommu_group_get(dev); - if (!iommu_group) - return false; - - group = vfio_group_get_from_iommu(iommu_group); - if (!group) { - iommu_group_put(iommu_group); - return false; - } - - device = vfio_group_get_device(group, dev); - if (!device) { - vfio_group_put(group); - iommu_group_put(iommu_group); - return false; - } - - vfio_device_put(device); - vfio_group_put(group); - iommu_group_put(iommu_group); - return true; -} - -/* - * Decrement the device reference count and wait for the device to be - * removed. Open file descriptors for the device... */ -void *vfio_del_group_dev(struct device *dev) -{ - struct vfio_device *device = dev_get_drvdata(dev); - struct vfio_group *group = device->group; - struct iommu_group *iommu_group = group->iommu_group; - void *device_data = device->device_data; - - vfio_device_put(device); - - /* TODO send a signal to encourage this to be released */ - wait_event(vfio.release_q, !vfio_dev_present(dev)); - - iommu_group_put(iommu_group); - - return device_data; -} -EXPORT_SYMBOL_GPL(vfio_del_group_dev); - -/** - * VFIO base fd, /dev/vfio/vfio - */ -static long vfio_ioctl_check_extension(struct vfio_container *container, - unsigned long arg) -{ - struct vfio_iommu_driver *driver = container->iommu_driver; - long ret = 0; - - switch (arg) { - /* No base extensions yet */ - default: - /* - * If no driver is set, poll all registered drivers for - * extensions and return the first positive result. If - * a driver is already set, further queries will be passed - * only to that driver. - */ - if (!driver) { - mutex_lock(&vfio.iommu_drivers_lock); - list_for_each_entry(driver, &vfio.iommu_drivers_list, - vfio_next) { - if (!try_module_get(driver->ops->owner)) - continue; - - ret = driver->ops->ioctl(NULL, - VFIO_CHECK_EXTENSION, - arg); - module_put(driver->ops->owner); - if (ret > 0) - break; - } - mutex_unlock(&vfio.iommu_drivers_lock); - } else - ret = driver->ops->ioctl(container->iommu_data, - VFIO_CHECK_EXTENSION, arg); - } - - return ret; -} - -/* hold container->group_lock */ -static int __vfio_container_attach_groups(struct vfio_container *container, - struct vfio_iommu_driver *driver, - void *data) -{ - struct vfio_group *group; - int ret = -ENODEV; - - list_for_each_entry(group, &container->group_list, container_next) { - ret = driver->ops->attach_group(data, group->iommu_group); - if (ret) - goto unwind; - } - - return ret; - -unwind: - list_for_each_entry_continue_reverse(group, &container->group_list, - container_next) { - driver->ops->detach_group(data, group->iommu_group); - } - - return ret; -} - -static long vfio_ioctl_set_iommu(struct vfio_container *container, - unsigned long arg) -{ - struct vfio_iommu_driver *driver; - long ret = -ENODEV; - - mutex_lock(&container->group_lock); - - /* - * The container is designed to be an unprivileged interface while - * the group can be assigned to specific users. Therefore, only by - * adding a group to a container does the user get the privilege of - * enabling the iommu, which may allocate finite resources. There - * is no unset_iommu, but by removing all the groups from a container, - * the container is deprivileged and returns to an unset state. - */ - if (list_empty(&container->group_list) || container->iommu_driver) { - mutex_unlock(&container->group_lock); - return -EINVAL; - } - - mutex_lock(&vfio.iommu_drivers_lock); - list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) { - void *data; - - if (!try_module_get(driver->ops->owner)) - continue; - - /* - * The arg magic for SET_IOMMU is the same as CHECK_EXTENSION, - * so test which iommu driver reported support for this - * extension and call open on them. We also pass them the - * magic, allowing a single driver to support multiple - * interfaces if they'd like. - */ - if (driver->ops->ioctl(NULL, VFIO_CHECK_EXTENSION, arg) <= 0) { - module_put(driver->ops->owner); - continue; - } - - /* module reference holds the driver we're working on */ - mutex_unlock(&vfio.iommu_drivers_lock); - - data = driver->ops->open(arg); - if (IS_ERR(data)) { - ret = PTR_ERR(data); - module_put(driver->ops->owner); - goto skip_drivers_unlock; - } - - ret = __vfio_container_attach_groups(container, driver, data); - if (!ret) { - container->iommu_driver = driver; - container->iommu_data = data; - } else { - driver->ops->release(data); - module_put(driver->ops->owner); - } - - goto skip_drivers_unlock; - } - - mutex_unlock(&vfio.iommu_drivers_lock); -skip_drivers_unlock: - mutex_unlock(&container->group_lock); - - return ret; -} - -static long vfio_fops_unl_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - struct vfio_container *container = filep->private_data; - struct vfio_iommu_driver *driver; - void *data; - long ret = -EINVAL; - - if (!container) - return ret; - - driver = container->iommu_driver; - data = container->iommu_data; - - switch (cmd) { - case VFIO_GET_API_VERSION: - ret = VFIO_API_VERSION; - break; - case VFIO_CHECK_EXTENSION: - ret = vfio_ioctl_check_extension(container, arg); - break; - case VFIO_SET_IOMMU: - ret = vfio_ioctl_set_iommu(container, arg); - break; - default: - if (driver) /* passthrough all unrecognized ioctls */ - ret = driver->ops->ioctl(data, cmd, arg); - } - - return ret; -} - -#ifdef CONFIG_COMPAT -static long vfio_fops_compat_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - arg = (unsigned long)compat_ptr(arg); - return vfio_fops_unl_ioctl(filep, cmd, arg); -} -#endif /* CONFIG_COMPAT */ - -static int vfio_fops_open(struct inode *inode, struct file *filep) -{ - struct vfio_container *container; - - container = kzalloc(sizeof(*container), GFP_KERNEL); - if (!container) - return -ENOMEM; - - INIT_LIST_HEAD(&container->group_list); - mutex_init(&container->group_lock); - kref_init(&container->kref); - - filep->private_data = container; - - return 0; -} - -static int vfio_fops_release(struct inode *inode, struct file *filep) -{ - struct vfio_container *container = filep->private_data; - - filep->private_data = NULL; - - vfio_container_put(container); - - return 0; -} - -/* - * Once an iommu driver is set, we optionally pass read/write/mmap - * on to the driver, allowing management interfaces beyond ioctl. - */ -static ssize_t vfio_fops_read(struct file *filep, char __user *buf, - size_t count, loff_t *ppos) -{ - struct vfio_container *container = filep->private_data; - struct vfio_iommu_driver *driver = container->iommu_driver; - - if (unlikely(!driver || !driver->ops->read)) - return -EINVAL; - - return driver->ops->read(container->iommu_data, buf, count, ppos); -} - -static ssize_t vfio_fops_write(struct file *filep, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct vfio_container *container = filep->private_data; - struct vfio_iommu_driver *driver = container->iommu_driver; - - if (unlikely(!driver || !driver->ops->write)) - return -EINVAL; - - return driver->ops->write(container->iommu_data, buf, count, ppos); -} - -static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma) -{ - struct vfio_container *container = filep->private_data; - struct vfio_iommu_driver *driver = container->iommu_driver; - - if (unlikely(!driver || !driver->ops->mmap)) - return -EINVAL; - - return driver->ops->mmap(container->iommu_data, vma); -} - -static const struct file_operations vfio_fops = { - .owner = THIS_MODULE, - .open = vfio_fops_open, - .release = vfio_fops_release, - .read = vfio_fops_read, - .write = vfio_fops_write, - .unlocked_ioctl = vfio_fops_unl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vfio_fops_compat_ioctl, -#endif - .mmap = vfio_fops_mmap, -}; - -/** - * VFIO Group fd, /dev/vfio/$GROUP - */ -static void __vfio_group_unset_container(struct vfio_group *group) -{ - struct vfio_container *container = group->container; - struct vfio_iommu_driver *driver; - - mutex_lock(&container->group_lock); - - driver = container->iommu_driver; - if (driver) - driver->ops->detach_group(container->iommu_data, - group->iommu_group); - - group->container = NULL; - list_del(&group->container_next); - - /* Detaching the last group deprivileges a container, remove iommu */ - if (driver && list_empty(&container->group_list)) { - driver->ops->release(container->iommu_data); - module_put(driver->ops->owner); - container->iommu_driver = NULL; - container->iommu_data = NULL; - } - - mutex_unlock(&container->group_lock); - - vfio_container_put(container); -} - -/* - * VFIO_GROUP_UNSET_CONTAINER should fail if there are other users or - * if there was no container to unset. Since the ioctl is called on - * the group, we know that still exists, therefore the only valid - * transition here is 1->0. - */ -static int vfio_group_unset_container(struct vfio_group *group) -{ - int users = atomic_cmpxchg(&group->container_users, 1, 0); - - if (!users) - return -EINVAL; - if (users != 1) - return -EBUSY; - - __vfio_group_unset_container(group); - - return 0; -} - -/* - * When removing container users, anything that removes the last user - * implicitly removes the group from the container. That is, if the - * group file descriptor is closed, as well as any device file descriptors, - * the group is free. - */ -static void vfio_group_try_dissolve_container(struct vfio_group *group) -{ - if (0 == atomic_dec_if_positive(&group->container_users)) - __vfio_group_unset_container(group); -} - -static int vfio_group_set_container(struct vfio_group *group, int container_fd) -{ - struct file *filep; - struct vfio_container *container; - struct vfio_iommu_driver *driver; - int ret = 0; - - if (atomic_read(&group->container_users)) - return -EINVAL; - - filep = fget(container_fd); - if (!filep) - return -EBADF; - - /* Sanity check, is this really our fd? */ - if (filep->f_op != &vfio_fops) { - fput(filep); - return -EINVAL; - } - - container = filep->private_data; - WARN_ON(!container); /* fget ensures we don't race vfio_release */ - - mutex_lock(&container->group_lock); - - driver = container->iommu_driver; - if (driver) { - ret = driver->ops->attach_group(container->iommu_data, - group->iommu_group); - if (ret) - goto unlock_out; - } - - group->container = container; - list_add(&group->container_next, &container->group_list); - - /* Get a reference on the container and mark a user within the group */ - vfio_container_get(container); - atomic_inc(&group->container_users); - -unlock_out: - mutex_unlock(&container->group_lock); - fput(filep); - - return ret; -} - -static bool vfio_group_viable(struct vfio_group *group) -{ - return (iommu_group_for_each_dev(group->iommu_group, - group, vfio_dev_viable) == 0); -} - -static const struct file_operations vfio_device_fops; - -static int vfio_group_get_device_fd(struct vfio_group *group, char *buf) -{ - struct vfio_device *device; - struct file *filep; - int ret = -ENODEV; - - if (0 == atomic_read(&group->container_users) || - !group->container->iommu_driver || !vfio_group_viable(group)) - return -EINVAL; - - mutex_lock(&group->device_lock); - list_for_each_entry(device, &group->device_list, group_next) { - if (strcmp(dev_name(device->dev), buf)) - continue; - - ret = device->ops->open(device->device_data); - if (ret) - break; - /* - * We can't use anon_inode_getfd() because we need to modify - * the f_mode flags directly to allow more than just ioctls - */ - ret = get_unused_fd(); - if (ret < 0) { - device->ops->release(device->device_data); - break; - } - - filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops, - device, O_RDWR); - if (IS_ERR(filep)) { - put_unused_fd(ret); - ret = PTR_ERR(filep); - device->ops->release(device->device_data); - break; - } - - /* - * TODO: add an anon_inode interface to do this. - * Appears to be missing by lack of need rather than - * explicitly prevented. Now there's need. - */ - filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); - - fd_install(ret, filep); - - vfio_device_get(device); - atomic_inc(&group->container_users); - break; - } - mutex_unlock(&group->device_lock); - - return ret; -} - -static long vfio_group_fops_unl_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - struct vfio_group *group = filep->private_data; - long ret = -ENOTTY; - - switch (cmd) { - case VFIO_GROUP_GET_STATUS: - { - struct vfio_group_status status; - unsigned long minsz; - - minsz = offsetofend(struct vfio_group_status, flags); - - if (copy_from_user(&status, (void __user *)arg, minsz)) - return -EFAULT; - - if (status.argsz < minsz) - return -EINVAL; - - status.flags = 0; - - if (vfio_group_viable(group)) - status.flags |= VFIO_GROUP_FLAGS_VIABLE; - - if (group->container) - status.flags |= VFIO_GROUP_FLAGS_CONTAINER_SET; - - if (copy_to_user((void __user *)arg, &status, minsz)) - return -EFAULT; - - ret = 0; - break; - } - case VFIO_GROUP_SET_CONTAINER: - { - int fd; - - if (get_user(fd, (int __user *)arg)) - return -EFAULT; - - if (fd < 0) - return -EINVAL; - - ret = vfio_group_set_container(group, fd); - break; - } - case VFIO_GROUP_UNSET_CONTAINER: - ret = vfio_group_unset_container(group); - break; - case VFIO_GROUP_GET_DEVICE_FD: - { - char *buf; - - buf = strndup_user((const char __user *)arg, PAGE_SIZE); - if (IS_ERR(buf)) - return PTR_ERR(buf); - - ret = vfio_group_get_device_fd(group, buf); - kfree(buf); - break; - } - } - - return ret; -} - -#ifdef CONFIG_COMPAT -static long vfio_group_fops_compat_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - arg = (unsigned long)compat_ptr(arg); - return vfio_group_fops_unl_ioctl(filep, cmd, arg); -} -#endif /* CONFIG_COMPAT */ - -static int vfio_group_fops_open(struct inode *inode, struct file *filep) -{ - struct vfio_group *group; - - group = vfio_group_get_from_minor(iminor(inode)); - if (!group) - return -ENODEV; - - if (group->container) { - vfio_group_put(group); - return -EBUSY; - } - - filep->private_data = group; - - return 0; -} - -static int vfio_group_fops_release(struct inode *inode, struct file *filep) -{ - struct vfio_group *group = filep->private_data; - - filep->private_data = NULL; - - vfio_group_try_dissolve_container(group); - - vfio_group_put(group); - - return 0; -} - -static const struct file_operations vfio_group_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = vfio_group_fops_unl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vfio_group_fops_compat_ioctl, -#endif - .open = vfio_group_fops_open, - .release = vfio_group_fops_release, -}; - -/** - * VFIO Device fd - */ -static int vfio_device_fops_release(struct inode *inode, struct file *filep) -{ - struct vfio_device *device = filep->private_data; - - device->ops->release(device->device_data); - - vfio_group_try_dissolve_container(device->group); - - vfio_device_put(device); - - return 0; -} - -static long vfio_device_fops_unl_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - struct vfio_device *device = filep->private_data; - - if (unlikely(!device->ops->ioctl)) - return -EINVAL; - - return device->ops->ioctl(device->device_data, cmd, arg); -} - -static ssize_t vfio_device_fops_read(struct file *filep, char __user *buf, - size_t count, loff_t *ppos) -{ - struct vfio_device *device = filep->private_data; - - if (unlikely(!device->ops->read)) - return -EINVAL; - - return device->ops->read(device->device_data, buf, count, ppos); -} - -static ssize_t vfio_device_fops_write(struct file *filep, - const char __user *buf, - size_t count, loff_t *ppos) -{ - struct vfio_device *device = filep->private_data; - - if (unlikely(!device->ops->write)) - return -EINVAL; - - return device->ops->write(device->device_data, buf, count, ppos); -} - -static int vfio_device_fops_mmap(struct file *filep, struct vm_area_struct *vma) -{ - struct vfio_device *device = filep->private_data; - - if (unlikely(!device->ops->mmap)) - return -EINVAL; - - return device->ops->mmap(device->device_data, vma); -} - -#ifdef CONFIG_COMPAT -static long vfio_device_fops_compat_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - arg = (unsigned long)compat_ptr(arg); - return vfio_device_fops_unl_ioctl(filep, cmd, arg); -} -#endif /* CONFIG_COMPAT */ - -static const struct file_operations vfio_device_fops = { - .owner = THIS_MODULE, - .release = vfio_device_fops_release, - .read = vfio_device_fops_read, - .write = vfio_device_fops_write, - .unlocked_ioctl = vfio_device_fops_unl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vfio_device_fops_compat_ioctl, -#endif - .mmap = vfio_device_fops_mmap, -}; - -/** - * Module/class support - */ -static char *vfio_devnode(struct device *dev, umode_t *mode) -{ - return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev)); -} - -static int __init vfio_init(void) -{ - int ret; - - idr_init(&vfio.group_idr); - mutex_init(&vfio.group_lock); - mutex_init(&vfio.iommu_drivers_lock); - INIT_LIST_HEAD(&vfio.group_list); - INIT_LIST_HEAD(&vfio.iommu_drivers_list); - init_waitqueue_head(&vfio.release_q); - - vfio.class = class_create(THIS_MODULE, "vfio"); - if (IS_ERR(vfio.class)) { - ret = PTR_ERR(vfio.class); - goto err_class; - } - - vfio.class->devnode = vfio_devnode; - - ret = alloc_chrdev_region(&vfio.devt, 0, MINORMASK, "vfio"); - if (ret) - goto err_base_chrdev; - - cdev_init(&vfio.cdev, &vfio_fops); - ret = cdev_add(&vfio.cdev, vfio.devt, 1); - if (ret) - goto err_base_cdev; - - vfio.dev = device_create(vfio.class, NULL, vfio.devt, NULL, "vfio"); - if (IS_ERR(vfio.dev)) { - ret = PTR_ERR(vfio.dev); - goto err_base_dev; - } - - /* /dev/vfio/$GROUP */ - cdev_init(&vfio.group_cdev, &vfio_group_fops); - ret = cdev_add(&vfio.group_cdev, - MKDEV(MAJOR(vfio.devt), 1), MINORMASK - 1); - if (ret) - goto err_groups_cdev; - - pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - - /* - * Attempt to load known iommu-drivers. This gives us a working - * environment without the user needing to explicitly load iommu - * drivers. - */ - request_module_nowait("vfio_iommu_type1"); - - return 0; - -err_groups_cdev: - device_destroy(vfio.class, vfio.devt); -err_base_dev: - cdev_del(&vfio.cdev); -err_base_cdev: - unregister_chrdev_region(vfio.devt, MINORMASK); -err_base_chrdev: - class_destroy(vfio.class); - vfio.class = NULL; -err_class: - return ret; -} - -static void __exit vfio_cleanup(void) -{ - WARN_ON(!list_empty(&vfio.group_list)); - - idr_destroy(&vfio.group_idr); - cdev_del(&vfio.group_cdev); - device_destroy(vfio.class, vfio.devt); - cdev_del(&vfio.cdev); - unregister_chrdev_region(vfio.devt, MINORMASK); - class_destroy(vfio.class); - vfio.class = NULL; -} - -module_init(vfio_init); -module_exit(vfio_cleanup); - -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/trunk/drivers/vfio/vfio_iommu_type1.c b/trunk/drivers/vfio/vfio_iommu_type1.c deleted file mode 100644 index 6f3fbc48a6c7..000000000000 --- a/trunk/drivers/vfio/vfio_iommu_type1.c +++ /dev/null @@ -1,753 +0,0 @@ -/* - * VFIO: IOMMU DMA mapping support for Type1 IOMMU - * - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * Author: Alex Williamson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from original vfio: - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * Author: Tom Lyon, pugs@cisco.com - * - * We arbitrarily define a Type1 IOMMU as one matching the below code. - * It could be called the x86 IOMMU as it's designed for AMD-Vi & Intel - * VT-d, but that makes it harder to re-use as theoretically anyone - * implementing a similar IOMMU could make use of this. We expect the - * IOMMU to support the IOMMU API and have few to no restrictions around - * the IOVA range that can be mapped. The Type1 IOMMU is currently - * optimized for relatively static mappings of a userspace process with - * userpsace pages pinned into memory. We also assume devices and IOMMU - * domains are PCI based as the IOMMU API is still centered around a - * device/bus interface rather than a group interface. - */ - -#include -#include -#include -#include -#include -#include -#include /* pci_bus_type */ -#include -#include -#include -#include -#include - -#define DRIVER_VERSION "0.2" -#define DRIVER_AUTHOR "Alex Williamson " -#define DRIVER_DESC "Type1 IOMMU driver for VFIO" - -static bool allow_unsafe_interrupts; -module_param_named(allow_unsafe_interrupts, - allow_unsafe_interrupts, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(allow_unsafe_interrupts, - "Enable VFIO IOMMU support for on platforms without interrupt remapping support."); - -struct vfio_iommu { - struct iommu_domain *domain; - struct mutex lock; - struct list_head dma_list; - struct list_head group_list; - bool cache; -}; - -struct vfio_dma { - struct list_head next; - dma_addr_t iova; /* Device address */ - unsigned long vaddr; /* Process virtual addr */ - long npage; /* Number of pages */ - int prot; /* IOMMU_READ/WRITE */ -}; - -struct vfio_group { - struct iommu_group *iommu_group; - struct list_head next; -}; - -/* - * This code handles mapping and unmapping of user data buffers - * into DMA'ble space using the IOMMU - */ - -#define NPAGE_TO_SIZE(npage) ((size_t)(npage) << PAGE_SHIFT) - -struct vwork { - struct mm_struct *mm; - long npage; - struct work_struct work; -}; - -/* delayed decrement/increment for locked_vm */ -static void vfio_lock_acct_bg(struct work_struct *work) -{ - struct vwork *vwork = container_of(work, struct vwork, work); - struct mm_struct *mm; - - mm = vwork->mm; - down_write(&mm->mmap_sem); - mm->locked_vm += vwork->npage; - up_write(&mm->mmap_sem); - mmput(mm); - kfree(vwork); -} - -static void vfio_lock_acct(long npage) -{ - struct vwork *vwork; - struct mm_struct *mm; - - if (!current->mm) - return; /* process exited */ - - if (down_write_trylock(¤t->mm->mmap_sem)) { - current->mm->locked_vm += npage; - up_write(¤t->mm->mmap_sem); - return; - } - - /* - * Couldn't get mmap_sem lock, so must setup to update - * mm->locked_vm later. If locked_vm were atomic, we - * wouldn't need this silliness - */ - vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL); - if (!vwork) - return; - mm = get_task_mm(current); - if (!mm) { - kfree(vwork); - return; - } - INIT_WORK(&vwork->work, vfio_lock_acct_bg); - vwork->mm = mm; - vwork->npage = npage; - schedule_work(&vwork->work); -} - -/* - * Some mappings aren't backed by a struct page, for example an mmap'd - * MMIO range for our own or another device. These use a different - * pfn conversion and shouldn't be tracked as locked pages. - */ -static bool is_invalid_reserved_pfn(unsigned long pfn) -{ - if (pfn_valid(pfn)) { - bool reserved; - struct page *tail = pfn_to_page(pfn); - struct page *head = compound_trans_head(tail); - reserved = !!(PageReserved(head)); - if (head != tail) { - /* - * "head" is not a dangling pointer - * (compound_trans_head takes care of that) - * but the hugepage may have been split - * from under us (and we may not hold a - * reference count on the head page so it can - * be reused before we run PageReferenced), so - * we've to check PageTail before returning - * what we just read. - */ - smp_rmb(); - if (PageTail(tail)) - return reserved; - } - return PageReserved(tail); - } - - return true; -} - -static int put_pfn(unsigned long pfn, int prot) -{ - if (!is_invalid_reserved_pfn(pfn)) { - struct page *page = pfn_to_page(pfn); - if (prot & IOMMU_WRITE) - SetPageDirty(page); - put_page(page); - return 1; - } - return 0; -} - -/* Unmap DMA region */ -static long __vfio_dma_do_unmap(struct vfio_iommu *iommu, dma_addr_t iova, - long npage, int prot) -{ - long i, unlocked = 0; - - for (i = 0; i < npage; i++, iova += PAGE_SIZE) { - unsigned long pfn; - - pfn = iommu_iova_to_phys(iommu->domain, iova) >> PAGE_SHIFT; - if (pfn) { - iommu_unmap(iommu->domain, iova, PAGE_SIZE); - unlocked += put_pfn(pfn, prot); - } - } - return unlocked; -} - -static void vfio_dma_unmap(struct vfio_iommu *iommu, dma_addr_t iova, - long npage, int prot) -{ - long unlocked; - - unlocked = __vfio_dma_do_unmap(iommu, iova, npage, prot); - vfio_lock_acct(-unlocked); -} - -static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn) -{ - struct page *page[1]; - struct vm_area_struct *vma; - int ret = -EFAULT; - - if (get_user_pages_fast(vaddr, 1, !!(prot & IOMMU_WRITE), page) == 1) { - *pfn = page_to_pfn(page[0]); - return 0; - } - - down_read(¤t->mm->mmap_sem); - - vma = find_vma_intersection(current->mm, vaddr, vaddr + 1); - - if (vma && vma->vm_flags & VM_PFNMAP) { - *pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; - if (is_invalid_reserved_pfn(*pfn)) - ret = 0; - } - - up_read(¤t->mm->mmap_sem); - - return ret; -} - -/* Map DMA region */ -static int __vfio_dma_map(struct vfio_iommu *iommu, dma_addr_t iova, - unsigned long vaddr, long npage, int prot) -{ - dma_addr_t start = iova; - long i, locked = 0; - int ret; - - /* Verify that pages are not already mapped */ - for (i = 0; i < npage; i++, iova += PAGE_SIZE) - if (iommu_iova_to_phys(iommu->domain, iova)) - return -EBUSY; - - iova = start; - - if (iommu->cache) - prot |= IOMMU_CACHE; - - /* - * XXX We break mappings into pages and use get_user_pages_fast to - * pin the pages in memory. It's been suggested that mlock might - * provide a more efficient mechanism, but nothing prevents the - * user from munlocking the pages, which could then allow the user - * access to random host memory. We also have no guarantee from the - * IOMMU API that the iommu driver can unmap sub-pages of previous - * mappings. This means we might lose an entire range if a single - * page within it is unmapped. Single page mappings are inefficient, - * but provide the most flexibility for now. - */ - for (i = 0; i < npage; i++, iova += PAGE_SIZE, vaddr += PAGE_SIZE) { - unsigned long pfn = 0; - - ret = vaddr_get_pfn(vaddr, prot, &pfn); - if (ret) { - __vfio_dma_do_unmap(iommu, start, i, prot); - return ret; - } - - /* - * Only add actual locked pages to accounting - * XXX We're effectively marking a page locked for every - * IOVA page even though it's possible the user could be - * backing multiple IOVAs with the same vaddr. This over- - * penalizes the user process, but we currently have no - * easy way to do this properly. - */ - if (!is_invalid_reserved_pfn(pfn)) - locked++; - - ret = iommu_map(iommu->domain, iova, - (phys_addr_t)pfn << PAGE_SHIFT, - PAGE_SIZE, prot); - if (ret) { - /* Back out mappings on error */ - put_pfn(pfn, prot); - __vfio_dma_do_unmap(iommu, start, i, prot); - return ret; - } - } - vfio_lock_acct(locked); - return 0; -} - -static inline bool ranges_overlap(dma_addr_t start1, size_t size1, - dma_addr_t start2, size_t size2) -{ - if (start1 < start2) - return (start2 - start1 < size1); - else if (start2 < start1) - return (start1 - start2 < size2); - return (size1 > 0 && size2 > 0); -} - -static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu, - dma_addr_t start, size_t size) -{ - struct vfio_dma *dma; - - list_for_each_entry(dma, &iommu->dma_list, next) { - if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage), - start, size)) - return dma; - } - return NULL; -} - -static long vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start, - size_t size, struct vfio_dma *dma) -{ - struct vfio_dma *split; - long npage_lo, npage_hi; - - /* Existing dma region is completely covered, unmap all */ - if (start <= dma->iova && - start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) { - vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot); - list_del(&dma->next); - npage_lo = dma->npage; - kfree(dma); - return npage_lo; - } - - /* Overlap low address of existing range */ - if (start <= dma->iova) { - size_t overlap; - - overlap = start + size - dma->iova; - npage_lo = overlap >> PAGE_SHIFT; - - vfio_dma_unmap(iommu, dma->iova, npage_lo, dma->prot); - dma->iova += overlap; - dma->vaddr += overlap; - dma->npage -= npage_lo; - return npage_lo; - } - - /* Overlap high address of existing range */ - if (start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) { - size_t overlap; - - overlap = dma->iova + NPAGE_TO_SIZE(dma->npage) - start; - npage_hi = overlap >> PAGE_SHIFT; - - vfio_dma_unmap(iommu, start, npage_hi, dma->prot); - dma->npage -= npage_hi; - return npage_hi; - } - - /* Split existing */ - npage_lo = (start - dma->iova) >> PAGE_SHIFT; - npage_hi = dma->npage - (size >> PAGE_SHIFT) - npage_lo; - - split = kzalloc(sizeof *split, GFP_KERNEL); - if (!split) - return -ENOMEM; - - vfio_dma_unmap(iommu, start, size >> PAGE_SHIFT, dma->prot); - - dma->npage = npage_lo; - - split->npage = npage_hi; - split->iova = start + size; - split->vaddr = dma->vaddr + NPAGE_TO_SIZE(npage_lo) + size; - split->prot = dma->prot; - list_add(&split->next, &iommu->dma_list); - return size >> PAGE_SHIFT; -} - -static int vfio_dma_do_unmap(struct vfio_iommu *iommu, - struct vfio_iommu_type1_dma_unmap *unmap) -{ - long ret = 0, npage = unmap->size >> PAGE_SHIFT; - struct vfio_dma *dma, *tmp; - uint64_t mask; - - mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1; - - if (unmap->iova & mask) - return -EINVAL; - if (unmap->size & mask) - return -EINVAL; - - /* XXX We still break these down into PAGE_SIZE */ - WARN_ON(mask & PAGE_MASK); - - mutex_lock(&iommu->lock); - - list_for_each_entry_safe(dma, tmp, &iommu->dma_list, next) { - if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage), - unmap->iova, unmap->size)) { - ret = vfio_remove_dma_overlap(iommu, unmap->iova, - unmap->size, dma); - if (ret > 0) - npage -= ret; - if (ret < 0 || npage == 0) - break; - } - } - mutex_unlock(&iommu->lock); - return ret > 0 ? 0 : (int)ret; -} - -static int vfio_dma_do_map(struct vfio_iommu *iommu, - struct vfio_iommu_type1_dma_map *map) -{ - struct vfio_dma *dma, *pdma = NULL; - dma_addr_t iova = map->iova; - unsigned long locked, lock_limit, vaddr = map->vaddr; - size_t size = map->size; - int ret = 0, prot = 0; - uint64_t mask; - long npage; - - mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1; - - /* READ/WRITE from device perspective */ - if (map->flags & VFIO_DMA_MAP_FLAG_WRITE) - prot |= IOMMU_WRITE; - if (map->flags & VFIO_DMA_MAP_FLAG_READ) - prot |= IOMMU_READ; - - if (!prot) - return -EINVAL; /* No READ/WRITE? */ - - if (vaddr & mask) - return -EINVAL; - if (iova & mask) - return -EINVAL; - if (size & mask) - return -EINVAL; - - /* XXX We still break these down into PAGE_SIZE */ - WARN_ON(mask & PAGE_MASK); - - /* Don't allow IOVA wrap */ - if (iova + size && iova + size < iova) - return -EINVAL; - - /* Don't allow virtual address wrap */ - if (vaddr + size && vaddr + size < vaddr) - return -EINVAL; - - npage = size >> PAGE_SHIFT; - if (!npage) - return -EINVAL; - - mutex_lock(&iommu->lock); - - if (vfio_find_dma(iommu, iova, size)) { - ret = -EBUSY; - goto out_lock; - } - - /* account for locked pages */ - locked = current->mm->locked_vm + npage; - lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { - pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", - __func__, rlimit(RLIMIT_MEMLOCK)); - ret = -ENOMEM; - goto out_lock; - } - - ret = __vfio_dma_map(iommu, iova, vaddr, npage, prot); - if (ret) - goto out_lock; - - /* Check if we abut a region below - nothing below 0 */ - if (iova) { - dma = vfio_find_dma(iommu, iova - 1, 1); - if (dma && dma->prot == prot && - dma->vaddr + NPAGE_TO_SIZE(dma->npage) == vaddr) { - - dma->npage += npage; - iova = dma->iova; - vaddr = dma->vaddr; - npage = dma->npage; - size = NPAGE_TO_SIZE(npage); - - pdma = dma; - } - } - - /* Check if we abut a region above - nothing above ~0 + 1 */ - if (iova + size) { - dma = vfio_find_dma(iommu, iova + size, 1); - if (dma && dma->prot == prot && - dma->vaddr == vaddr + size) { - - dma->npage += npage; - dma->iova = iova; - dma->vaddr = vaddr; - - /* - * If merged above and below, remove previously - * merged entry. New entry covers it. - */ - if (pdma) { - list_del(&pdma->next); - kfree(pdma); - } - pdma = dma; - } - } - - /* Isolated, new region */ - if (!pdma) { - dma = kzalloc(sizeof *dma, GFP_KERNEL); - if (!dma) { - ret = -ENOMEM; - vfio_dma_unmap(iommu, iova, npage, prot); - goto out_lock; - } - - dma->npage = npage; - dma->iova = iova; - dma->vaddr = vaddr; - dma->prot = prot; - list_add(&dma->next, &iommu->dma_list); - } - -out_lock: - mutex_unlock(&iommu->lock); - return ret; -} - -static int vfio_iommu_type1_attach_group(void *iommu_data, - struct iommu_group *iommu_group) -{ - struct vfio_iommu *iommu = iommu_data; - struct vfio_group *group, *tmp; - int ret; - - group = kzalloc(sizeof(*group), GFP_KERNEL); - if (!group) - return -ENOMEM; - - mutex_lock(&iommu->lock); - - list_for_each_entry(tmp, &iommu->group_list, next) { - if (tmp->iommu_group == iommu_group) { - mutex_unlock(&iommu->lock); - kfree(group); - return -EINVAL; - } - } - - /* - * TODO: Domain have capabilities that might change as we add - * groups (see iommu->cache, currently never set). Check for - * them and potentially disallow groups to be attached when it - * would change capabilities (ugh). - */ - ret = iommu_attach_group(iommu->domain, iommu_group); - if (ret) { - mutex_unlock(&iommu->lock); - kfree(group); - return ret; - } - - group->iommu_group = iommu_group; - list_add(&group->next, &iommu->group_list); - - mutex_unlock(&iommu->lock); - - return 0; -} - -static void vfio_iommu_type1_detach_group(void *iommu_data, - struct iommu_group *iommu_group) -{ - struct vfio_iommu *iommu = iommu_data; - struct vfio_group *group; - - mutex_lock(&iommu->lock); - - list_for_each_entry(group, &iommu->group_list, next) { - if (group->iommu_group == iommu_group) { - iommu_detach_group(iommu->domain, iommu_group); - list_del(&group->next); - kfree(group); - break; - } - } - - mutex_unlock(&iommu->lock); -} - -static void *vfio_iommu_type1_open(unsigned long arg) -{ - struct vfio_iommu *iommu; - - if (arg != VFIO_TYPE1_IOMMU) - return ERR_PTR(-EINVAL); - - iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); - if (!iommu) - return ERR_PTR(-ENOMEM); - - INIT_LIST_HEAD(&iommu->group_list); - INIT_LIST_HEAD(&iommu->dma_list); - mutex_init(&iommu->lock); - - /* - * Wish we didn't have to know about bus_type here. - */ - iommu->domain = iommu_domain_alloc(&pci_bus_type); - if (!iommu->domain) { - kfree(iommu); - return ERR_PTR(-EIO); - } - - /* - * Wish we could specify required capabilities rather than create - * a domain, see what comes out and hope it doesn't change along - * the way. Fortunately we know interrupt remapping is global for - * our iommus. - */ - if (!allow_unsafe_interrupts && - !iommu_domain_has_cap(iommu->domain, IOMMU_CAP_INTR_REMAP)) { - pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n", - __func__); - iommu_domain_free(iommu->domain); - kfree(iommu); - return ERR_PTR(-EPERM); - } - - return iommu; -} - -static void vfio_iommu_type1_release(void *iommu_data) -{ - struct vfio_iommu *iommu = iommu_data; - struct vfio_group *group, *group_tmp; - struct vfio_dma *dma, *dma_tmp; - - list_for_each_entry_safe(group, group_tmp, &iommu->group_list, next) { - iommu_detach_group(iommu->domain, group->iommu_group); - list_del(&group->next); - kfree(group); - } - - list_for_each_entry_safe(dma, dma_tmp, &iommu->dma_list, next) { - vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot); - list_del(&dma->next); - kfree(dma); - } - - iommu_domain_free(iommu->domain); - iommu->domain = NULL; - kfree(iommu); -} - -static long vfio_iommu_type1_ioctl(void *iommu_data, - unsigned int cmd, unsigned long arg) -{ - struct vfio_iommu *iommu = iommu_data; - unsigned long minsz; - - if (cmd == VFIO_CHECK_EXTENSION) { - switch (arg) { - case VFIO_TYPE1_IOMMU: - return 1; - default: - return 0; - } - } else if (cmd == VFIO_IOMMU_GET_INFO) { - struct vfio_iommu_type1_info info; - - minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes); - - if (copy_from_user(&info, (void __user *)arg, minsz)) - return -EFAULT; - - if (info.argsz < minsz) - return -EINVAL; - - info.flags = 0; - - info.iova_pgsizes = iommu->domain->ops->pgsize_bitmap; - - return copy_to_user((void __user *)arg, &info, minsz); - - } else if (cmd == VFIO_IOMMU_MAP_DMA) { - struct vfio_iommu_type1_dma_map map; - uint32_t mask = VFIO_DMA_MAP_FLAG_READ | - VFIO_DMA_MAP_FLAG_WRITE; - - minsz = offsetofend(struct vfio_iommu_type1_dma_map, size); - - if (copy_from_user(&map, (void __user *)arg, minsz)) - return -EFAULT; - - if (map.argsz < minsz || map.flags & ~mask) - return -EINVAL; - - return vfio_dma_do_map(iommu, &map); - - } else if (cmd == VFIO_IOMMU_UNMAP_DMA) { - struct vfio_iommu_type1_dma_unmap unmap; - - minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size); - - if (copy_from_user(&unmap, (void __user *)arg, minsz)) - return -EFAULT; - - if (unmap.argsz < minsz || unmap.flags) - return -EINVAL; - - return vfio_dma_do_unmap(iommu, &unmap); - } - - return -ENOTTY; -} - -static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = { - .name = "vfio-iommu-type1", - .owner = THIS_MODULE, - .open = vfio_iommu_type1_open, - .release = vfio_iommu_type1_release, - .ioctl = vfio_iommu_type1_ioctl, - .attach_group = vfio_iommu_type1_attach_group, - .detach_group = vfio_iommu_type1_detach_group, -}; - -static int __init vfio_iommu_type1_init(void) -{ - if (!iommu_present(&pci_bus_type)) - return -ENODEV; - - return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1); -} - -static void __exit vfio_iommu_type1_cleanup(void) -{ - vfio_unregister_iommu_driver(&vfio_iommu_driver_ops_type1); -} - -module_init(vfio_iommu_type1_init); -module_exit(vfio_iommu_type1_cleanup); - -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/trunk/drivers/video/aty/aty128fb.c b/trunk/drivers/video/aty/aty128fb.c index 747442d2c0f6..b0b2ac335347 100644 --- a/trunk/drivers/video/aty/aty128fb.c +++ b/trunk/drivers/video/aty/aty128fb.c @@ -90,8 +90,7 @@ #undef DEBUG #ifdef DEBUG -#define DBG(fmt, args...) \ - printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args); +#define DBG(fmt, args...) printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args); #else #define DBG(fmt, args...) #endif @@ -450,9 +449,8 @@ static int aty128_decode_var(struct fb_var_screeninfo *var, struct aty128fb_par *par); #if 0 static void __devinit aty128_get_pllinfo(struct aty128fb_par *par, - void __iomem *bios); -static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev, - const struct aty128fb_par *par); + void __iomem *bios); +static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev, const struct aty128fb_par *par); #endif static void aty128_timings(struct aty128fb_par *par); static void aty128_init_engine(struct aty128fb_par *par); @@ -781,8 +779,7 @@ static u32 depth_to_dst(u32 depth) #ifndef __sparc__ -static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, - struct pci_dev *dev) +static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev) { u16 dptr; u8 rom_type; @@ -814,14 +811,13 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, /* Look for the PCI data to check the ROM type */ dptr = BIOS_IN16(0x18); - /* Check the PCI data signature. If it's wrong, we still assume a normal - * x86 ROM for now, until I've verified this works everywhere. - * The goal here is more to phase out Open Firmware images. + /* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM + * for now, until I've verified this works everywhere. The goal here is more + * to phase out Open Firmware images. * - * Currently, we only look at the first PCI data, we could iteratre and - * deal with them all, and we should use fb_bios_start relative to start - * of image and not relative start of ROM, but so far, I never found a - * dual-image ATI card. + * Currently, we only look at the first PCI data, we could iteratre and deal with + * them all, and we should use fb_bios_start relative to start of image and not + * relative start of ROM, but so far, I never found a dual-image ATI card * * typedef struct { * u32 signature; + 0x00 @@ -856,8 +852,7 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n"); goto failed; default: - printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", - rom_type); + printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", rom_type); goto failed; } anyway: @@ -868,8 +863,7 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, return NULL; } -static void __devinit aty128_get_pllinfo(struct aty128fb_par *par, - unsigned char __iomem *bios) +static void __devinit aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios) { unsigned int bios_hdr; unsigned int bios_pll; @@ -1253,13 +1247,10 @@ static int aty128_crtc_to_var(const struct aty128_crtc *crtc, static void aty128_set_crt_enable(struct aty128fb_par *par, int on) { if (on) { - aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | - CRT_CRTC_ON); - aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | - DAC_PALETTE2_SNOOP_EN)); + aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | CRT_CRTC_ON); + aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | DAC_PALETTE2_SNOOP_EN)); } else - aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & - ~CRT_CRTC_ON); + aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & ~CRT_CRTC_ON); } static void aty128_set_lcd_enable(struct aty128fb_par *par, int on) @@ -1290,8 +1281,7 @@ static void aty128_set_lcd_enable(struct aty128fb_par *par, int on) } } -static void aty128_set_pll(struct aty128_pll *pll, - const struct aty128fb_par *par) +static void aty128_set_pll(struct aty128_pll *pll, const struct aty128fb_par *par) { u32 div3; @@ -1376,8 +1366,7 @@ static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll, } -static int aty128_pll_to_var(const struct aty128_pll *pll, - struct fb_var_screeninfo *var) +static int aty128_pll_to_var(const struct aty128_pll *pll, struct fb_var_screeninfo *var) { var->pixclock = 100000000 / pll->vclk; @@ -1523,8 +1512,7 @@ static int aty128fb_set_par(struct fb_info *info) * encode/decode the User Defined Part of the Display */ -static int aty128_decode_var(struct fb_var_screeninfo *var, - struct aty128fb_par *par) +static int aty128_decode_var(struct fb_var_screeninfo *var, struct aty128fb_par *par) { int err; struct aty128_crtc crtc; @@ -1571,8 +1559,7 @@ static int aty128_encode_var(struct fb_var_screeninfo *var, } -static int aty128fb_check_var(struct fb_var_screeninfo *var, - struct fb_info *info) +static int aty128fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct aty128fb_par par; int err; @@ -1588,8 +1575,7 @@ static int aty128fb_check_var(struct fb_var_screeninfo *var, /* * Pan or Wrap the Display */ -static int aty128fb_pan_display(struct fb_var_screeninfo *var, - struct fb_info *fb) +static int aty128fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb) { struct aty128fb_par *par = fb->par; u32 xoffset, yoffset; @@ -1608,8 +1594,7 @@ static int aty128fb_pan_display(struct fb_var_screeninfo *var, par->crtc.xoffset = xoffset; par->crtc.yoffset = yoffset; - offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3)) - & ~7; + offset = ((yoffset * par->crtc.vxres + xoffset)*(par->crtc.bpp >> 3)) & ~7; if (par->crtc.bpp == 24) offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */ @@ -1635,13 +1620,11 @@ static void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue, * do mirroring */ - aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | - DAC_PALETTE_ACCESS_CNTL); + aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL); aty_st_8(PALETTE_INDEX, regno); aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue); #endif - aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & - ~DAC_PALETTE_ACCESS_CNTL); + aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL); } aty_st_8(PALETTE_INDEX, regno); @@ -1770,8 +1753,7 @@ static int aty128_bl_update_status(struct backlight_device *bd) aty_st_le32(LVDS_GEN_CNTL, reg); } reg &= ~LVDS_BL_MOD_LEVEL_MASK; - reg |= (aty128_bl_get_level_brightness(par, level) << - LVDS_BL_MOD_LEVEL_SHIFT); + reg |= (aty128_bl_get_level_brightness(par, level) << LVDS_BL_MOD_LEVEL_SHIFT); #ifdef BACKLIGHT_LVDS_OFF reg |= LVDS_ON | LVDS_EN; reg &= ~LVDS_DISPLAY_DIS; @@ -1782,8 +1764,7 @@ static int aty128_bl_update_status(struct backlight_device *bd) #endif } else { reg &= ~LVDS_BL_MOD_LEVEL_MASK; - reg |= (aty128_bl_get_level_brightness(par, 0) << - LVDS_BL_MOD_LEVEL_SHIFT); + reg |= (aty128_bl_get_level_brightness(par, 0) << LVDS_BL_MOD_LEVEL_SHIFT); #ifdef BACKLIGHT_LVDS_OFF reg |= LVDS_DISPLAY_DIS; aty_st_le32(LVDS_GEN_CNTL, reg); @@ -1888,8 +1869,7 @@ static void aty128_early_resume(void *data) } #endif /* CONFIG_PPC_PMAC */ -static int __devinit aty128_init(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent) { struct fb_info *info = pci_get_drvdata(pdev); struct aty128fb_par *par = info->par; @@ -1907,8 +1887,7 @@ static int __devinit aty128_init(struct pci_dev *pdev, /* range check to make sure */ if (ent->driver_data < ARRAY_SIZE(r128_family)) - strlcat(video_card, r128_family[ent->driver_data], - sizeof(video_card)); + strlcat(video_card, r128_family[ent->driver_data], sizeof(video_card)); printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev); @@ -1932,11 +1911,11 @@ static int __devinit aty128_init(struct pci_dev *pdev, /* Indicate sleep capability */ if (par->chip_gen == rage_M3) { pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1); -#if 0 /* Disable the early video resume hack for now as it's causing problems, - * among others we now rely on the PCI core restoring the config space - * for us, which isn't the case with that hack, and that code path causes - * various things to be called with interrupts off while they shouldn't. - * I'm leaving the code in as it can be useful for debugging purposes +#if 0 /* Disable the early video resume hack for now as it's causing problems, among + * others we now rely on the PCI core restoring the config space for us, which + * isn't the case with that hack, and that code path causes various things to + * be called with interrupts off while they shouldn't. I'm leaving the code in + * as it can be useful for debugging purposes */ pmac_set_early_video_resume(aty128_early_resume, par); #endif @@ -1974,11 +1953,11 @@ static int __devinit aty128_init(struct pci_dev *pdev, default_vmode = VMODE_1152_768_60; if (default_cmode > 16) - default_cmode = CMODE_32; + default_cmode = CMODE_32; else if (default_cmode > 8) - default_cmode = CMODE_16; + default_cmode = CMODE_16; else - default_cmode = CMODE_8; + default_cmode = CMODE_8; if (mac_vmode_to_var(default_vmode, default_cmode, &var)) var = default_var; @@ -2039,8 +2018,7 @@ static int __devinit aty128_init(struct pci_dev *pdev, #ifdef CONFIG_PCI /* register a card ++ajoshi */ -static int __devinit aty128_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int __devinit aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { unsigned long fb_addr, reg_addr; struct aty128fb_par *par; @@ -2340,39 +2318,39 @@ static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty, u_int width, u_int height, struct fb_info_aty128 *par) { - u32 save_dp_datatype, save_dp_cntl, dstval; - - if (!width || !height) - return; - - dstval = depth_to_dst(par->current_par.crtc.depth); - if (dstval == DST_24BPP) { - srcx *= 3; - dstx *= 3; - width *= 3; - } else if (dstval == -EINVAL) { - printk("aty128fb: invalid depth or RGBA\n"); - return; - } - - wait_for_fifo(2, par); - save_dp_datatype = aty_ld_le32(DP_DATATYPE); - save_dp_cntl = aty_ld_le32(DP_CNTL); - - wait_for_fifo(6, par); - aty_st_le32(SRC_Y_X, (srcy << 16) | srcx); - aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT); - aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM); - aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR); - - aty_st_le32(DST_Y_X, (dsty << 16) | dstx); - aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width); - - par->blitter_may_be_busy = 1; - - wait_for_fifo(2, par); - aty_st_le32(DP_DATATYPE, save_dp_datatype); - aty_st_le32(DP_CNTL, save_dp_cntl); + u32 save_dp_datatype, save_dp_cntl, dstval; + + if (!width || !height) + return; + + dstval = depth_to_dst(par->current_par.crtc.depth); + if (dstval == DST_24BPP) { + srcx *= 3; + dstx *= 3; + width *= 3; + } else if (dstval == -EINVAL) { + printk("aty128fb: invalid depth or RGBA\n"); + return; + } + + wait_for_fifo(2, par); + save_dp_datatype = aty_ld_le32(DP_DATATYPE); + save_dp_cntl = aty_ld_le32(DP_CNTL); + + wait_for_fifo(6, par); + aty_st_le32(SRC_Y_X, (srcy << 16) | srcx); + aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT); + aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM); + aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR); + + aty_st_le32(DST_Y_X, (dsty << 16) | dstx); + aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width); + + par->blitter_may_be_busy = 1; + + wait_for_fifo(2, par); + aty_st_le32(DP_DATATYPE, save_dp_datatype); + aty_st_le32(DP_CNTL, save_dp_cntl); } @@ -2380,17 +2358,17 @@ static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty, * Text mode accelerated functions */ -static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, - int dx, int height, int width) +static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) { - sx *= fontwidth(p); - sy *= fontheight(p); - dx *= fontwidth(p); - dy *= fontheight(p); - width *= fontwidth(p); - height *= fontheight(p); - - aty128_rectcopy(sx, sy, dx, dy, width, height, + sx *= fontwidth(p); + sy *= fontheight(p); + dx *= fontwidth(p); + dy *= fontheight(p); + width *= fontwidth(p); + height *= fontheight(p); + + aty128_rectcopy(sx, sy, dx, dy, width, height, (struct fb_info_aty128 *)p->fb_info); } #endif /* 0 */ diff --git a/trunk/drivers/video/da8xx-fb.c b/trunk/drivers/video/da8xx-fb.c index 7ae9d53f2bf1..47118c75a4c0 100644 --- a/trunk/drivers/video/da8xx-fb.c +++ b/trunk/drivers/video/da8xx-fb.c @@ -30,10 +30,7 @@ #include #include #include -#include #include -#include -#include #include