diff --git a/[refs] b/[refs]
index bb997f8b2c5d..7e0c27c1905b 100644
--- a/[refs]
+++ b/[refs]
@@ -1,2 +1,2 @@
---
-refs/heads/master: 946fef4e14ebc2f14ab05f54789843621fe87f60
+refs/heads/master: 50d9a126240f9961cfdd063336bbeb91f77a7dce
diff --git a/trunk/Documentation/DocBook/videobook.tmpl b/trunk/Documentation/DocBook/videobook.tmpl
index b629da33951d..b3d93ee27693 100644
--- a/trunk/Documentation/DocBook/videobook.tmpl
+++ b/trunk/Documentation/DocBook/videobook.tmpl
@@ -96,7 +96,6 @@ static struct video_device my_radio
{
"My radio",
VID_TYPE_TUNER,
- VID_HARDWARE_MYRADIO,
radio_open.
radio_close,
NULL, /* no read */
@@ -118,13 +117,6 @@ static struct video_device my_radio
indicates that the device can be tuned. Clearly our radio is going to have some
way to change channel so it is tuneable.
-
- The VID_HARDWARE_ types are unique to each device. Numbers are assigned by
- alan@redhat.com when device drivers are going to be released. Until then you
- can pull a suitably large number out of your hat and use it. 10000 should be
- safe for a very long time even allowing for the huge number of vendors
- making new and different radio cards at the moment.
-
We declare an open and close routine, but we do not need read or write,
which are used to read and write video data to or from the card itself. As
@@ -844,7 +836,6 @@ static struct video_device my_camera
"My Camera",
VID_TYPE_OVERLAY|VID_TYPE_SCALES|\
VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY,
- VID_HARDWARE_MYCAMERA,
camera_open.
camera_close,
camera_read, /* no read */
diff --git a/trunk/Documentation/RCU/RTFP.txt b/trunk/Documentation/RCU/RTFP.txt
index 6221464d1a7e..39ad8f56783a 100644
--- a/trunk/Documentation/RCU/RTFP.txt
+++ b/trunk/Documentation/RCU/RTFP.txt
@@ -9,8 +9,8 @@ The first thing resembling RCU was published in 1980, when Kung and Lehman
[Kung80] recommended use of a garbage collector to defer destruction
of nodes in a parallel binary search tree in order to simplify its
implementation. This works well in environments that have garbage
-collectors, but current production garbage collectors incur significant
-read-side overhead.
+collectors, but most production garbage collectors incur significant
+overhead.
In 1982, Manber and Ladner [Manber82,Manber84] recommended deferring
destruction until all threads running at that time have terminated, again
@@ -99,16 +99,25 @@ locking, reduces contention, reduces memory latency for readers, and
parallelizes pipeline stalls and memory latency for writers. However,
these techniques still impose significant read-side overhead in the
form of memory barriers. Researchers at Sun worked along similar lines
-in the same timeframe [HerlihyLM02,HerlihyLMS03]. These techniques
-can be thought of as inside-out reference counts, where the count is
-represented by the number of hazard pointers referencing a given data
-structure (rather than the more conventional counter field within the
-data structure itself).
+in the same timeframe [HerlihyLM02]. These techniques can be thought
+of as inside-out reference counts, where the count is represented by the
+number of hazard pointers referencing a given data structure (rather than
+the more conventional counter field within the data structure itself).
+
+By the same token, RCU can be thought of as a "bulk reference count",
+where some form of reference counter covers all reference by a given CPU
+or thread during a set timeframe. This timeframe is related to, but
+not necessarily exactly the same as, an RCU grace period. In classic
+RCU, the reference counter is the per-CPU bit in the "bitmask" field,
+and each such bit covers all references that might have been made by
+the corresponding CPU during the prior grace period. Of course, RCU
+can be thought of in other terms as well.
In 2003, the K42 group described how RCU could be used to create
-hot-pluggable implementations of operating-system functions. Later that
-year saw a paper describing an RCU implementation of System V IPC
-[Arcangeli03], and an introduction to RCU in Linux Journal [McKenney03a].
+hot-pluggable implementations of operating-system functions [Appavoo03a].
+Later that year saw a paper describing an RCU implementation of System
+V IPC [Arcangeli03], and an introduction to RCU in Linux Journal
+[McKenney03a].
2004 has seen a Linux-Journal article on use of RCU in dcache
[McKenney04a], a performance comparison of locking to RCU on several
@@ -117,10 +126,19 @@ number of operating-system kernels [PaulEdwardMcKenneyPhD], a paper
describing how to make RCU safe for soft-realtime applications [Sarma04c],
and a paper describing SELinux performance with RCU [JamesMorris04b].
-2005 has seen further adaptation of RCU to realtime use, permitting
+2005 brought further adaptation of RCU to realtime use, permitting
preemption of RCU realtime critical sections [PaulMcKenney05a,
PaulMcKenney05b].
+2006 saw the first best-paper award for an RCU paper [ThomasEHart2006a],
+as well as further work on efficient implementations of preemptible
+RCU [PaulEMcKenney2006b], but priority-boosting of RCU read-side critical
+sections proved elusive. An RCU implementation permitting general
+blocking in read-side critical sections appeared [PaulEMcKenney2006c],
+Robert Olsson described an RCU-protected trie-hash combination
+[RobertOlsson2006a].
+
+
Bibtex Entries
@article{Kung80
@@ -203,6 +221,41 @@ Bibtex Entries
,Address="New Orleans, LA"
}
+@conference{Pu95a,
+Author = "Calton Pu and Tito Autrey and Andrew Black and Charles Consel and
+Crispin Cowan and Jon Inouye and Lakshmi Kethana and Jonathan Walpole and
+Ke Zhang",
+Title = "Optimistic Incremental Specialization: Streamlining a Commercial
+Operating System",
+Booktitle = "15\textsuperscript{th} ACM Symposium on
+Operating Systems Principles (SOSP'95)",
+address = "Copper Mountain, CO",
+month="December",
+year="1995",
+pages="314-321",
+annotation="
+ Uses a replugger, but with a flag to signal when people are
+ using the resource at hand. Only one reader at a time.
+"
+}
+
+@conference{Cowan96a,
+Author = "Crispin Cowan and Tito Autrey and Charles Krasic and
+Calton Pu and Jonathan Walpole",
+Title = "Fast Concurrent Dynamic Linking for an Adaptive Operating System",
+Booktitle = "International Conference on Configurable Distributed Systems
+(ICCDS'96)",
+address = "Annapolis, MD",
+month="May",
+year="1996",
+pages="108",
+isbn="0-8186-7395-8",
+annotation="
+ Uses a replugger, but with a counter to signal when people are
+ using the resource at hand. Allows multiple readers.
+"
+}
+
@techreport{Slingwine95
,author="John D. Slingwine and Paul E. McKenney"
,title="Apparatus and Method for Achieving Reduced Overhead Mutual
@@ -312,6 +365,49 @@ Andrea Arcangeli and Andi Kleen and Orran Krieger and Rusty Russell"
[Viewed June 23, 2004]"
}
+@conference{Michael02a
+,author="Maged M. Michael"
+,title="Safe Memory Reclamation for Dynamic Lock-Free Objects Using Atomic
+Reads and Writes"
+,Year="2002"
+,Month="August"
+,booktitle="{Proceedings of the 21\textsuperscript{st} Annual ACM
+Symposium on Principles of Distributed Computing}"
+,pages="21-30"
+,annotation="
+ Each thread keeps an array of pointers to items that it is
+ currently referencing. Sort of an inside-out garbage collection
+ mechanism, but one that requires the accessing code to explicitly
+ state its needs. Also requires read-side memory barriers on
+ most architectures.
+"
+}
+
+@conference{Michael02b
+,author="Maged M. Michael"
+,title="High Performance Dynamic Lock-Free Hash Tables and List-Based Sets"
+,Year="2002"
+,Month="August"
+,booktitle="{Proceedings of the 14\textsuperscript{th} Annual ACM
+Symposium on Parallel
+Algorithms and Architecture}"
+,pages="73-82"
+,annotation="
+ Like the title says...
+"
+}
+
+@InProceedings{HerlihyLM02
+,author={Maurice Herlihy and Victor Luchangco and Mark Moir}
+,title="The Repeat Offender Problem: A Mechanism for Supporting Dynamic-Sized,
+Lock-Free Data Structures"
+,booktitle={Proceedings of 16\textsuperscript{th} International
+Symposium on Distributed Computing}
+,year=2002
+,month="October"
+,pages="339-353"
+}
+
@article{Appavoo03a
,author="J. Appavoo and K. Hui and C. A. N. Soules and R. W. Wisniewski and
D. M. {Da Silva} and O. Krieger and M. A. Auslander and D. J. Edelsohn and
@@ -447,3 +543,95 @@ Oregon Health and Sciences University"
Realtime turns into making RCU yet more realtime friendly.
"
}
+
+@conference{ThomasEHart2006a
+,Author="Thomas E. Hart and Paul E. McKenney and Angela Demke Brown"
+,Title="Making Lockless Synchronization Fast: Performance Implications
+of Memory Reclamation"
+,Booktitle="20\textsuperscript{th} {IEEE} International Parallel and
+Distributed Processing Symposium"
+,month="April"
+,year="2006"
+,day="25-29"
+,address="Rhodes, Greece"
+,annotation="
+ Compares QSBR (AKA "classic RCU"), HPBR, EBR, and lock-free
+ reference counting.
+"
+}
+
+@Conference{PaulEMcKenney2006b
+,Author="Paul E. McKenney and Dipankar Sarma and Ingo Molnar and
+Suparna Bhattacharya"
+,Title="Extending RCU for Realtime and Embedded Workloads"
+,Booktitle="{Ottawa Linux Symposium}"
+,Month="July"
+,Year="2006"
+,pages="v2 123-138"
+,note="Available:
+\url{http://www.linuxsymposium.org/2006/view_abstract.php?content_key=184}
+\url{http://www.rdrop.com/users/paulmck/RCU/OLSrtRCU.2006.08.11a.pdf}
+[Viewed January 1, 2007]"
+,annotation="
+ Described how to improve the -rt implementation of realtime RCU.
+"
+}
+
+@unpublished{PaulEMcKenney2006c
+,Author="Paul E. McKenney"
+,Title="Sleepable {RCU}"
+,month="October"
+,day="9"
+,year="2006"
+,note="Available:
+\url{http://lwn.net/Articles/202847/}
+Revised:
+\url{http://www.rdrop.com/users/paulmck/RCU/srcu.2007.01.14a.pdf}
+[Viewed August 21, 2006]"
+,annotation="
+ LWN article introducing SRCU.
+"
+}
+
+@unpublished{RobertOlsson2006a
+,Author="Robert Olsson and Stefan Nilsson"
+,Title="{TRASH}: A dynamic {LC}-trie and hash data structure"
+,month="August"
+,day="18"
+,year="2006"
+,note="Available:
+\url{http://www.nada.kth.se/~snilsson/public/papers/trash/trash.pdf}
+[Viewed February 24, 2007]"
+,annotation="
+ RCU-protected dynamic trie-hash combination.
+"
+}
+
+@unpublished{ThomasEHart2007a
+,Author="Thomas E. Hart and Paul E. McKenney and Angela Demke Brown and Jonathan Walpole"
+,Title="Performance of memory reclamation for lockless synchronization"
+,journal="J. Parallel Distrib. Comput."
+,year="2007"
+,note="To appear in J. Parallel Distrib. Comput.
+ \url{doi=10.1016/j.jpdc.2007.04.010}"
+,annotation={
+ Compares QSBR (AKA "classic RCU"), HPBR, EBR, and lock-free
+ reference counting. Journal version of ThomasEHart2006a.
+}
+}
+
+@unpublished{PaulEMcKenney2007QRCUspin
+,Author="Paul E. McKenney"
+,Title="Using Promela and Spin to verify parallel algorithms"
+,month="August"
+,day="1"
+,year="2007"
+,note="Available:
+\url{http://lwn.net/Articles/243851/}
+[Viewed September 8, 2007]"
+,annotation="
+ LWN article describing Promela and spin, and also using Oleg
+ Nesterov's QRCU as an example (with Paul McKenney's fastpath).
+"
+}
+
diff --git a/trunk/Documentation/RCU/rcu.txt b/trunk/Documentation/RCU/rcu.txt
index f84407cba816..95821a29ae41 100644
--- a/trunk/Documentation/RCU/rcu.txt
+++ b/trunk/Documentation/RCU/rcu.txt
@@ -36,6 +36,14 @@ o How can the updater tell when a grace period has completed
executed in user mode, or executed in the idle loop, we can
safely free up that item.
+ Preemptible variants of RCU (CONFIG_PREEMPT_RCU) get the
+ same effect, but require that the readers manipulate CPU-local
+ counters. These counters allow limited types of blocking
+ within RCU read-side critical sections. SRCU also uses
+ CPU-local counters, and permits general blocking within
+ RCU read-side critical sections. These two variants of
+ RCU detect grace periods by sampling these counters.
+
o If I am running on a uniprocessor kernel, which can only do one
thing at a time, why should I wait for a grace period?
@@ -46,7 +54,10 @@ o How can I see where RCU is currently used in the Linux kernel?
Search for "rcu_read_lock", "rcu_read_unlock", "call_rcu",
"rcu_read_lock_bh", "rcu_read_unlock_bh", "call_rcu_bh",
"srcu_read_lock", "srcu_read_unlock", "synchronize_rcu",
- "synchronize_net", and "synchronize_srcu".
+ "synchronize_net", "synchronize_srcu", and the other RCU
+ primitives. Or grab one of the cscope databases from:
+
+ http://www.rdrop.com/users/paulmck/RCU/linuxusage/rculocktab.html
o What guidelines should I follow when writing code that uses RCU?
@@ -67,7 +78,11 @@ o I hear that RCU is patented? What is with that?
o I hear that RCU needs work in order to support realtime kernels?
- Yes, work in progress.
+ This work is largely completed. Realtime-friendly RCU can be
+ enabled via the CONFIG_PREEMPT_RCU kernel configuration parameter.
+ However, work is in progress for enabling priority boosting of
+ preempted RCU read-side critical sections.This is needed if you
+ have CPU-bound realtime threads.
o Where can I find more information on RCU?
diff --git a/trunk/Documentation/RCU/torture.txt b/trunk/Documentation/RCU/torture.txt
index 25a3c3f7d378..2967a65269d8 100644
--- a/trunk/Documentation/RCU/torture.txt
+++ b/trunk/Documentation/RCU/torture.txt
@@ -46,12 +46,13 @@ stat_interval The number of seconds between output of torture
shuffle_interval
The number of seconds to keep the test threads affinitied
- to a particular subset of the CPUs. Used in conjunction
- with test_no_idle_hz.
+ to a particular subset of the CPUs, defaults to 5 seconds.
+ Used in conjunction with test_no_idle_hz.
test_no_idle_hz Whether or not to test the ability of RCU to operate in
a kernel that disables the scheduling-clock interrupt to
idle CPUs. Boolean parameter, "1" to test, "0" otherwise.
+ Defaults to omitting this test.
torture_type The type of RCU to test: "rcu" for the rcu_read_lock() API,
"rcu_sync" for rcu_read_lock() with synchronous reclamation,
@@ -82,8 +83,6 @@ be evident. ;-)
The entries are as follows:
-o "ggp": The number of counter flips (or batches) since boot.
-
o "rtc": The hexadecimal address of the structure currently visible
to readers.
@@ -117,8 +116,8 @@ o "Reader Pipe": Histogram of "ages" of structures seen by readers.
o "Reader Batch": Another histogram of "ages" of structures seen
by readers, but in terms of counter flips (or batches) rather
than in terms of grace periods. The legal number of non-zero
- entries is again two. The reason for this separate view is
- that it is easier to get the third entry to show up in the
+ entries is again two. The reason for this separate view is that
+ it is sometimes easier to get the third entry to show up in the
"Reader Batch" list than in the "Reader Pipe" list.
o "Free-Block Circulation": Shows the number of torture structures
diff --git a/trunk/Documentation/cpu-hotplug.txt b/trunk/Documentation/cpu-hotplug.txt
index a741f658a3c9..fb94f5a71b68 100644
--- a/trunk/Documentation/cpu-hotplug.txt
+++ b/trunk/Documentation/cpu-hotplug.txt
@@ -109,12 +109,13 @@ Never use anything other than cpumask_t to represent bitmap of CPUs.
for_each_cpu_mask(x,mask) - Iterate over some random collection of cpu mask.
#include
- lock_cpu_hotplug() and unlock_cpu_hotplug():
+ get_online_cpus() and put_online_cpus():
-The above calls are used to inhibit cpu hotplug operations. While holding the
-cpucontrol mutex, cpu_online_map will not change. If you merely need to avoid
-cpus going away, you could also use preempt_disable() and preempt_enable()
-for those sections. Just remember the critical section cannot call any
+The above calls are used to inhibit cpu hotplug operations. While the
+cpu_hotplug.refcount is non zero, the cpu_online_map will not change.
+If you merely need to avoid cpus going away, you could also use
+preempt_disable() and preempt_enable() for those sections.
+Just remember the critical section cannot call any
function that can sleep or schedule this process away. The preempt_disable()
will work as long as stop_machine_run() is used to take a cpu down.
diff --git a/trunk/Documentation/dvb/bt8xx.txt b/trunk/Documentation/dvb/bt8xx.txt
index ecb47adda063..b7b1d1b1da46 100644
--- a/trunk/Documentation/dvb/bt8xx.txt
+++ b/trunk/Documentation/dvb/bt8xx.txt
@@ -78,6 +78,18 @@ Example:
For a full list of card ID's please see Documentation/video4linux/CARDLIST.bttv.
In case of further problems please subscribe and send questions to the mailing list: linux-dvb@linuxtv.org.
+2c) Probing the cards with broken PCI subsystem ID
+--------------------------------------------------
+There are some TwinHan cards that the EEPROM has become corrupted for some
+reason. The cards do not have correct PCI subsystem ID. But we can force
+probing the cards with broken PCI subsystem ID
+
+ $ echo 109e 0878 $subvendor $subdevice > \
+ /sys/bus/pci/drivers/bt878/new_id
+
+109e: PCI_VENDOR_ID_BROOKTREE
+0878: PCI_DEVICE_ID_BROOKTREE_878
+
Authors: Richard Walker,
Jamie Honan,
Michael Hunold,
diff --git a/trunk/Documentation/feature-removal-schedule.txt b/trunk/Documentation/feature-removal-schedule.txt
index 20c4c8bac9d7..9b8291f4c211 100644
--- a/trunk/Documentation/feature-removal-schedule.txt
+++ b/trunk/Documentation/feature-removal-schedule.txt
@@ -295,16 +295,6 @@ Who: linuxppc-dev@ozlabs.org
---------------------------
-What: mthca driver's MSI support
-When: January 2008
-Files: drivers/infiniband/hw/mthca/*.[ch]
-Why: All mthca hardware also supports MSI-X, which provides
- strictly more functionality than MSI. So there is no point in
- having both MSI-X and MSI support in the driver.
-Who: Roland Dreier
-
----------------------------
-
What: sk98lin network driver
When: Feburary 2008
Why: In kernel tree version of driver is unmaintained. Sk98lin driver
diff --git a/trunk/Documentation/filesystems/ocfs2.txt b/trunk/Documentation/filesystems/ocfs2.txt
index ed55238023a9..c318a8bbb1ef 100644
--- a/trunk/Documentation/filesystems/ocfs2.txt
+++ b/trunk/Documentation/filesystems/ocfs2.txt
@@ -35,7 +35,6 @@ Features which OCFS2 does not support yet:
- Directory change notification (F_NOTIFY)
- Distributed Caching (F_SETLEASE/F_GETLEASE/break_lease)
- POSIX ACLs
- - readpages / writepages (not user visible)
Mount options
=============
@@ -62,3 +61,18 @@ data=writeback Data ordering is not preserved, data may be written
preferred_slot=0(*) During mount, try to use this filesystem slot first. If
it is in use by another node, the first empty one found
will be chosen. Invalid values will be ignored.
+commit=nrsec (*) Ocfs2 can be told to sync all its data and metadata
+ every 'nrsec' seconds. The default value is 5 seconds.
+ This means that if you lose your power, you will lose
+ as much as the latest 5 seconds of work (your
+ filesystem will not be damaged though, thanks to the
+ journaling). This default value (or any low value)
+ will hurt performance, but it's good for data-safety.
+ Setting it to 0 will have the same effect as leaving
+ it at the default (5 seconds).
+ Setting it to very large values will improve
+ performance.
+localalloc=8(*) Allows custom localalloc size in MB. If the value is too
+ large, the fs will silently revert it to the default.
+ Localalloc is not enabled for local mounts.
+localflocks This disables cluster aware flock.
diff --git a/trunk/Documentation/ioctl-number.txt b/trunk/Documentation/ioctl-number.txt
index 5c7fbf9d96b4..c18363bd8d11 100644
--- a/trunk/Documentation/ioctl-number.txt
+++ b/trunk/Documentation/ioctl-number.txt
@@ -138,6 +138,7 @@ Code Seq# Include File Comments
'm' 00-1F net/irda/irmod.h conflict!
'n' 00-7F linux/ncp_fs.h
'n' E0-FF video/matrox.h matroxfb
+'o' 00-1F fs/ocfs2/ocfs2_fs.h OCFS2
'p' 00-0F linux/phantom.h conflict! (OpenHaptics needs this)
'p' 00-3F linux/mc146818rtc.h conflict!
'p' 40-7F linux/nvram.h
diff --git a/trunk/Documentation/video4linux/CARDLIST.cx23885 b/trunk/Documentation/video4linux/CARDLIST.cx23885
index 00cb646a4bde..0924e6e142c4 100644
--- a/trunk/Documentation/video4linux/CARDLIST.cx23885
+++ b/trunk/Documentation/video4linux/CARDLIST.cx23885
@@ -1,5 +1,7 @@
0 -> UNKNOWN/GENERIC [0070:3400]
1 -> Hauppauge WinTV-HVR1800lp [0070:7600]
- 2 -> Hauppauge WinTV-HVR1800 [0070:7800,0070:7801]
+ 2 -> Hauppauge WinTV-HVR1800 [0070:7800,0070:7801,0070:7809]
3 -> Hauppauge WinTV-HVR1250 [0070:7911]
4 -> DViCO FusionHDTV5 Express [18ac:d500]
+ 5 -> Hauppauge WinTV-HVR1500Q [0070:7790,0070:7797]
+ 6 -> Hauppauge WinTV-HVR1500 [0070:7710,0070:7717]
diff --git a/trunk/Documentation/video4linux/CARDLIST.cx88 b/trunk/Documentation/video4linux/CARDLIST.cx88
index 82ac8250e978..bc5593bd9704 100644
--- a/trunk/Documentation/video4linux/CARDLIST.cx88
+++ b/trunk/Documentation/video4linux/CARDLIST.cx88
@@ -56,3 +56,4 @@
55 -> Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM [c180:c980]
56 -> Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder [0070:9600,0070:9601,0070:9602]
57 -> ADS Tech Instant Video PCI [1421:0390]
+ 58 -> Pinnacle PCTV HD 800i [11bd:0051]
diff --git a/trunk/Documentation/video4linux/CARDLIST.em28xx b/trunk/Documentation/video4linux/CARDLIST.em28xx
index 37f0e3cedf43..6a8469f2bcae 100644
--- a/trunk/Documentation/video4linux/CARDLIST.em28xx
+++ b/trunk/Documentation/video4linux/CARDLIST.em28xx
@@ -1,14 +1,17 @@
0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800]
- 1 -> Unknown EM2820/2840 video grabber (em2820/em2840)
+ 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2750,eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036]
3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208]
- 4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200]
- 5 -> MSI VOX USB 2.0 (em2820/em2840) [eb1a:2820]
+ 4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201]
+ 5 -> MSI VOX USB 2.0 (em2820/em2840)
6 -> Terratec Cinergy 200 USB (em2800)
7 -> Leadtek Winfast USB II (em2800)
8 -> Kworld USB2800 (em2800)
- 9 -> Pinnacle Dazzle DVC 90 (em2820/em2840) [2304:0207]
- 10 -> Hauppauge WinTV HVR 900 (em2880)
- 11 -> Terratec Hybrid XS (em2880)
+ 9 -> Pinnacle Dazzle DVC 90/DVC 100 (em2820/em2840) [2304:0207,2304:021a]
+ 10 -> Hauppauge WinTV HVR 900 (em2880) [2040:6500]
+ 11 -> Terratec Hybrid XS (em2880) [0ccd:0042]
12 -> Kworld PVR TV 2800 RF (em2820/em2840)
- 13 -> Terratec Prodigy XS (em2880)
+ 13 -> Terratec Prodigy XS (em2880) [0ccd:0047]
+ 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840)
+ 15 -> V-Gear PocketTV (em2800)
+ 16 -> Hauppauge WinTV HVR 950 (em2880) [2040:6513]
diff --git a/trunk/Documentation/video4linux/CARDLIST.ivtv b/trunk/Documentation/video4linux/CARDLIST.ivtv
index ddd76a0eb100..a019e27e42b3 100644
--- a/trunk/Documentation/video4linux/CARDLIST.ivtv
+++ b/trunk/Documentation/video4linux/CARDLIST.ivtv
@@ -16,3 +16,9 @@
16 -> GOTVIEW PCI DVD2 Deluxe [ffac:0600]
17 -> Yuan MPC622 [ff01:d998]
18 -> Digital Cowboy DCT-MTVP1 [1461:bfff]
+19 -> Yuan PG600V2/GotView PCI DVD Lite [ffab:0600,ffad:0600]
+20 -> Club3D ZAP-TV1x01 [ffab:0600]
+21 -> AverTV MCE 116 Plus [1461:c439]
+22 -> ASUS Falcon2 [1043:4b66,1043:462e,1043:4b2e]
+23 -> AverMedia PVR-150 Plus [1461:c035]
+24 -> AverMedia EZMaker PCI Deluxe [1461:c03f]
diff --git a/trunk/Documentation/video4linux/CARDLIST.saa7134 b/trunk/Documentation/video4linux/CARDLIST.saa7134
index a14545300e4c..5d3b6b4d2515 100644
--- a/trunk/Documentation/video4linux/CARDLIST.saa7134
+++ b/trunk/Documentation/video4linux/CARDLIST.saa7134
@@ -80,7 +80,7 @@
79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)
80 -> ASUS Digimatrix TV [1043:0210]
81 -> Philips Tiger reference design [1131:2018]
- 82 -> MSI TV@Anywhere plus [1462:6231]
+ 82 -> MSI TV@Anywhere plus [1462:6231,1462:8624]
83 -> Terratec Cinergy 250 PCI TV [153b:1160]
84 -> LifeView FlyDVB Trio [5168:0319]
85 -> AverTV DVB-T 777 [1461:2c05,1461:2c05]
@@ -102,7 +102,7 @@
101 -> Pinnacle PCTV 310i [11bd:002f]
102 -> Avermedia AVerTV Studio 507 [1461:9715]
103 -> Compro Videomate DVB-T200A
-104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid [0070:6701]
+104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid [0070:6700,0070:6701,0070:6702,0070:6703,0070:6704,0070:6705]
105 -> Terratec Cinergy HT PCMCIA [153b:1172]
106 -> Encore ENLTV [1131:2342,1131:2341,3016:2344]
107 -> Encore ENLTV-FM [1131:230f]
@@ -116,3 +116,16 @@
115 -> Sabrent PCMCIA TV-PCB05 [0919:2003]
116 -> 10MOONS TM300 TV Card [1131:2304]
117 -> Avermedia Super 007 [1461:f01d]
+118 -> Beholder BeholdTV 401 [0000:4016]
+119 -> Beholder BeholdTV 403 [0000:4036]
+120 -> Beholder BeholdTV 403 FM [0000:4037]
+121 -> Beholder BeholdTV 405 [0000:4050]
+122 -> Beholder BeholdTV 405 FM [0000:4051]
+123 -> Beholder BeholdTV 407 [0000:4070]
+124 -> Beholder BeholdTV 407 FM [0000:4071]
+125 -> Beholder BeholdTV 409 [0000:4090]
+126 -> Beholder BeholdTV 505 FM/RDS [0000:5051,0000:505B,5ace:5050]
+127 -> Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM [0000:5071,0000:507B,5ace:5070,5ace:5090]
+128 -> Beholder BeholdTV Columbus TVFM [0000:5201]
+129 -> Beholder BeholdTV 607 / BeholdTV 609 [5ace:6070,5ace:6071,5ace:6072,5ace:6073,5ace:6090,5ace:6091,5ace:6092,5ace:6093]
+130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193]
diff --git a/trunk/Documentation/video4linux/CARDLIST.tuner b/trunk/Documentation/video4linux/CARDLIST.tuner
index a88c02d23805..0e2394695bb8 100644
--- a/trunk/Documentation/video4linux/CARDLIST.tuner
+++ b/trunk/Documentation/video4linux/CARDLIST.tuner
@@ -52,7 +52,7 @@ tuner=50 - TCL 2002N
tuner=51 - Philips PAL/SECAM_D (FM 1256 I-H3)
tuner=52 - Thomson DTT 7610 (ATSC/NTSC)
tuner=53 - Philips FQ1286
-tuner=54 - tda8290+75
+tuner=54 - Philips/NXP TDA 8290/8295 + 8275/8275A/18271
tuner=55 - TCL 2002MB
tuner=56 - Philips PAL/SECAM multi (FQ1216AME MK4)
tuner=57 - Philips FQ1236A MK4
@@ -69,7 +69,8 @@ tuner=67 - Philips TD1316 Hybrid Tuner
tuner=68 - Philips TUV1236D ATSC/NTSC dual in
tuner=69 - Tena TNF 5335 and similar models
tuner=70 - Samsung TCPN 2121P30A
-tuner=71 - Xceive xc3028
+tuner=71 - Xceive xc2028/xc3028 tuner
tuner=72 - Thomson FE6600
tuner=73 - Samsung TCPG 6121P30A
tuner=75 - Philips TEA5761 FM Radio
+tuner=76 - Xceive 5000 tuner
diff --git a/trunk/Documentation/video4linux/CARDLIST.usbvision b/trunk/Documentation/video4linux/CARDLIST.usbvision
index 3d6850ef0245..0b72d3fee17e 100644
--- a/trunk/Documentation/video4linux/CARDLIST.usbvision
+++ b/trunk/Documentation/video4linux/CARDLIST.usbvision
@@ -62,3 +62,4 @@
61 -> Pinnacle Studio Linx Video input cable (PAL) [2304:0301]
62 -> Pinnacle PCTV Bungee USB (PAL) FM [2304:0419]
63 -> Hauppauge WinTv-USB [2400:4200]
+ 64 -> Pinnacle Studio PCTV USB (NTSC) FM V3 [2304:0113]
diff --git a/trunk/Documentation/video4linux/extract_xc3028.pl b/trunk/Documentation/video4linux/extract_xc3028.pl
new file mode 100644
index 000000000000..cced8ac5c543
--- /dev/null
+++ b/trunk/Documentation/video4linux/extract_xc3028.pl
@@ -0,0 +1,926 @@
+#!/usr/bin/perl
+
+# Copyright (c) Mauro Carvalho Chehab
+# Released under GPLv2
+#
+# In order to use, you need to:
+# 1) Download the windows driver with something like:
+# wget http://www.steventoth.net/linux/xc5000/HVR-12x0-14x0-17x0_1_25_25271_WHQL.zip
+# 2) Extract the file hcw85bda.sys from the zip into the current dir:
+# unzip -j HVR-12x0-14x0-17x0_1_25_25271_WHQL.zip Driver85/hcw85bda.sys
+# 3) run the script:
+# ./extract_xc3028.pl
+# 4) copy the generated file:
+# cp xc3028-v27.fw /lib/firmware
+
+#use strict;
+use IO::Handle;
+
+my $debug=0;
+
+sub verify ($$)
+{
+ my ($filename, $hash) = @_;
+ my ($testhash);
+
+ if (system("which md5sum > /dev/null 2>&1")) {
+ die "This firmware requires the md5sum command - see http://www.gnu.org/software/coreutils/\n";
+ }
+
+ open(CMD, "md5sum ".$filename."|");
+ $testhash = ;
+ $testhash =~ /([a-zA-Z0-9]*)/;
+ $testhash = $1;
+ close CMD;
+ die "Hash of extracted file does not match (found $testhash, expected $hash!\n" if ($testhash ne $hash);
+}
+
+sub get_hunk ($$)
+{
+ my ($offset, $length) = @_;
+ my ($chunklength, $buf, $rcount, $out);
+
+ sysseek(INFILE, $offset, SEEK_SET);
+ while ($length > 0) {
+ # Calc chunk size
+ $chunklength = 2048;
+ $chunklength = $length if ($chunklength > $length);
+
+ $rcount = sysread(INFILE, $buf, $chunklength);
+ die "Ran out of data\n" if ($rcount != $chunklength);
+ $out .= $buf;
+ $length -= $rcount;
+ }
+ return $out;
+}
+
+sub write_le16($)
+{
+ my $val = shift;
+ my $msb = ($val >> 8) &0xff;
+ my $lsb = $val & 0xff;
+
+ syswrite(OUTFILE, chr($lsb).chr($msb));
+}
+
+sub write_le32($)
+{
+ my $val = shift;
+ my $l3 = ($val >> 24) & 0xff;
+ my $l2 = ($val >> 16) & 0xff;
+ my $l1 = ($val >> 8) & 0xff;
+ my $l0 = $val & 0xff;
+
+ syswrite(OUTFILE, chr($l0).chr($l1).chr($l2).chr($l3));
+}
+
+sub write_le64($$)
+{
+ my $msb_val = shift;
+ my $lsb_val = shift;
+ my $l7 = ($msb_val >> 24) & 0xff;
+ my $l6 = ($msb_val >> 16) & 0xff;
+ my $l5 = ($msb_val >> 8) & 0xff;
+ my $l4 = $msb_val & 0xff;
+
+ my $l3 = ($lsb_val >> 24) & 0xff;
+ my $l2 = ($lsb_val >> 16) & 0xff;
+ my $l1 = ($lsb_val >> 8) & 0xff;
+ my $l0 = $lsb_val & 0xff;
+
+ syswrite(OUTFILE,
+ chr($l0).chr($l1).chr($l2).chr($l3).
+ chr($l4).chr($l5).chr($l6).chr($l7));
+}
+
+sub write_hunk($$)
+{
+ my ($offset, $length) = @_;
+ my $out = get_hunk($offset, $length);
+
+ printf "(len %d) ",$length if ($debug);
+
+ for (my $i=0;$i<$length;$i++) {
+ printf "%02x ",ord(substr($out,$i,1)) if ($debug);
+ }
+ printf "\n" if ($debug);
+
+ syswrite(OUTFILE, $out);
+}
+
+sub write_hunk_fix_endian($$)
+{
+ my ($offset, $length) = @_;
+ my $out = get_hunk($offset, $length);
+
+ printf "(len_fix %d) ",$length if ($debug);
+
+ for (my $i=0;$i<$length;$i++) {
+ printf "%02x ",ord(substr($out,$i,1)) if ($debug);
+ }
+ printf "\n" if ($debug);
+
+ my $i=0;
+ while ($i<$length) {
+ my $size = ord(substr($out,$i,1))*256+ord(substr($out,$i+1,1));
+ syswrite(OUTFILE, substr($out,$i+1,1));
+ syswrite(OUTFILE, substr($out,$i,1));
+ $i+=2;
+ if ($size>0 && $size <0x8000) {
+ for (my $j=0;$j<$size;$j++) {
+ syswrite(OUTFILE, substr($out,$j+$i,1));
+ }
+ $i+=$size;
+ }
+ }
+}
+
+sub main_firmware($$$$)
+{
+ my $out;
+ my $j=0;
+ my $outfile = shift;
+ my $name = shift;
+ my $version = shift;
+ my $nr_desc = shift;
+
+ for ($j = length($name); $j <32; $j++) {
+ $name = $name.chr(0);
+}
+
+ open OUTFILE, ">$outfile";
+ syswrite(OUTFILE, $name);
+ write_le16($version);
+ write_le16($nr_desc);
+
+ #
+ # Firmware 0, type: BASE FW F8MHZ (0x00000003), id: (0000000000000000), size: 8718
+ #
+
+ write_le32(0x00000003); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(8718); # Size
+ write_hunk_fix_endian(813432, 8718);
+
+ #
+ # Firmware 1, type: BASE FW F8MHZ MTS (0x00000007), id: (0000000000000000), size: 8712
+ #
+
+ write_le32(0x00000007); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(8712); # Size
+ write_hunk_fix_endian(822152, 8712);
+
+ #
+ # Firmware 2, type: BASE FW FM (0x00000401), id: (0000000000000000), size: 8562
+ #
+
+ write_le32(0x00000401); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(8562); # Size
+ write_hunk_fix_endian(830872, 8562);
+
+ #
+ # Firmware 3, type: BASE FW FM INPUT1 (0x00000c01), id: (0000000000000000), size: 8576
+ #
+
+ write_le32(0x00000c01); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(8576); # Size
+ write_hunk_fix_endian(839440, 8576);
+
+ #
+ # Firmware 4, type: BASE FW (0x00000001), id: (0000000000000000), size: 8706
+ #
+
+ write_le32(0x00000001); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(8706); # Size
+ write_hunk_fix_endian(848024, 8706);
+
+ #
+ # Firmware 5, type: BASE FW MTS (0x00000005), id: (0000000000000000), size: 8682
+ #
+
+ write_le32(0x00000005); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(8682); # Size
+ write_hunk_fix_endian(856736, 8682);
+
+ #
+ # Firmware 6, type: STD FW (0x00000000), id: PAL/BG A2/A (0000000100000007), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000001, 0x00000007); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(865424, 161);
+
+ #
+ # Firmware 7, type: STD FW MTS (0x00000004), id: PAL/BG A2/A (0000000100000007), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000001, 0x00000007); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(865592, 169);
+
+ #
+ # Firmware 8, type: STD FW (0x00000000), id: PAL/BG A2/B (0000000200000007), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000002, 0x00000007); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(865424, 161);
+
+ #
+ # Firmware 9, type: STD FW MTS (0x00000004), id: PAL/BG A2/B (0000000200000007), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000002, 0x00000007); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(865592, 169);
+
+ #
+ # Firmware 10, type: STD FW (0x00000000), id: PAL/BG NICAM/A (0000000400000007), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000004, 0x00000007); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(866112, 161);
+
+ #
+ # Firmware 11, type: STD FW MTS (0x00000004), id: PAL/BG NICAM/A (0000000400000007), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000004, 0x00000007); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(866280, 169);
+
+ #
+ # Firmware 12, type: STD FW (0x00000000), id: PAL/BG NICAM/B (0000000800000007), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000008, 0x00000007); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(866112, 161);
+
+ #
+ # Firmware 13, type: STD FW MTS (0x00000004), id: PAL/BG NICAM/B (0000000800000007), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000008, 0x00000007); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(866280, 169);
+
+ #
+ # Firmware 14, type: STD FW (0x00000000), id: PAL/DK A2 (00000003000000e0), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000003, 0x000000e0); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(866800, 161);
+
+ #
+ # Firmware 15, type: STD FW MTS (0x00000004), id: PAL/DK A2 (00000003000000e0), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000003, 0x000000e0); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(866968, 169);
+
+ #
+ # Firmware 16, type: STD FW (0x00000000), id: PAL/DK NICAM (0000000c000000e0), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x0000000c, 0x000000e0); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(867144, 161);
+
+ #
+ # Firmware 17, type: STD FW MTS (0x00000004), id: PAL/DK NICAM (0000000c000000e0), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x0000000c, 0x000000e0); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(867312, 169);
+
+ #
+ # Firmware 18, type: STD FW (0x00000000), id: SECAM/K1 (0000000000200000), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000000, 0x00200000); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(867488, 161);
+
+ #
+ # Firmware 19, type: STD FW MTS (0x00000004), id: SECAM/K1 (0000000000200000), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000000, 0x00200000); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(867656, 169);
+
+ #
+ # Firmware 20, type: STD FW (0x00000000), id: SECAM/K3 (0000000004000000), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000000, 0x04000000); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(867832, 161);
+
+ #
+ # Firmware 21, type: STD FW MTS (0x00000004), id: SECAM/K3 (0000000004000000), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000000, 0x04000000); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(868000, 169);
+
+ #
+ # Firmware 22, type: STD FW D2633 DTV6 ATSC (0x00010030), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00010030); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(868176, 149);
+
+ #
+ # Firmware 23, type: STD FW D2620 DTV6 QAM (0x00000068), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00000068); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(868336, 149);
+
+ #
+ # Firmware 24, type: STD FW D2633 DTV6 QAM (0x00000070), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00000070); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(868488, 149);
+
+ #
+ # Firmware 25, type: STD FW D2620 DTV7 (0x00000088), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00000088); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(868648, 149);
+
+ #
+ # Firmware 26, type: STD FW D2633 DTV7 (0x00000090), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00000090); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(868800, 149);
+
+ #
+ # Firmware 27, type: STD FW D2620 DTV78 (0x00000108), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00000108); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(868960, 149);
+
+ #
+ # Firmware 28, type: STD FW D2633 DTV78 (0x00000110), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00000110); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(869112, 149);
+
+ #
+ # Firmware 29, type: STD FW D2620 DTV8 (0x00000208), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00000208); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(868648, 149);
+
+ #
+ # Firmware 30, type: STD FW D2633 DTV8 (0x00000210), id: (0000000000000000), size: 149
+ #
+
+ write_le32(0x00000210); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(149); # Size
+ write_hunk_fix_endian(868800, 149);
+
+ #
+ # Firmware 31, type: STD FW FM (0x00000400), id: (0000000000000000), size: 135
+ #
+
+ write_le32(0x00000400); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le32(135); # Size
+ write_hunk_fix_endian(869584, 135);
+
+ #
+ # Firmware 32, type: STD FW (0x00000000), id: PAL/I (0000000000000010), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000000, 0x00000010); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(869728, 161);
+
+ #
+ # Firmware 33, type: STD FW MTS (0x00000004), id: PAL/I (0000000000000010), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000000, 0x00000010); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(869896, 169);
+
+ #
+ # Firmware 34, type: STD FW (0x00000000), id: SECAM/L AM (0000001000400000), size: 169
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000010, 0x00400000); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(870072, 169);
+
+ #
+ # Firmware 35, type: STD FW (0x00000000), id: SECAM/L NICAM (0000000c00400000), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x0000000c, 0x00400000); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(870248, 161);
+
+ #
+ # Firmware 36, type: STD FW (0x00000000), id: SECAM/Lc (0000000000800000), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000000, 0x00800000); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(870416, 161);
+
+ #
+ # Firmware 37, type: STD FW (0x00000000), id: NTSC/M Kr (0000000000008000), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000000, 0x00008000); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(870584, 161);
+
+ #
+ # Firmware 38, type: STD FW LCD (0x00001000), id: NTSC/M Kr (0000000000008000), size: 161
+ #
+
+ write_le32(0x00001000); # Type
+ write_le64(0x00000000, 0x00008000); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(870752, 161);
+
+ #
+ # Firmware 39, type: STD FW LCD NOGD (0x00003000), id: NTSC/M Kr (0000000000008000), size: 161
+ #
+
+ write_le32(0x00003000); # Type
+ write_le64(0x00000000, 0x00008000); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(870920, 161);
+
+ #
+ # Firmware 40, type: STD FW MTS (0x00000004), id: NTSC/M Kr (0000000000008000), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000000, 0x00008000); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(871088, 169);
+
+ #
+ # Firmware 41, type: STD FW (0x00000000), id: NTSC PAL/M PAL/N (000000000000b700), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000000, 0x0000b700); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(871264, 161);
+
+ #
+ # Firmware 42, type: STD FW LCD (0x00001000), id: NTSC PAL/M PAL/N (000000000000b700), size: 161
+ #
+
+ write_le32(0x00001000); # Type
+ write_le64(0x00000000, 0x0000b700); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(871432, 161);
+
+ #
+ # Firmware 43, type: STD FW LCD NOGD (0x00003000), id: NTSC PAL/M PAL/N (000000000000b700), size: 161
+ #
+
+ write_le32(0x00003000); # Type
+ write_le64(0x00000000, 0x0000b700); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(871600, 161);
+
+ #
+ # Firmware 44, type: STD FW (0x00000000), id: NTSC/M Jp (0000000000002000), size: 161
+ #
+
+ write_le32(0x00000000); # Type
+ write_le64(0x00000000, 0x00002000); # ID
+ write_le32(161); # Size
+ write_hunk_fix_endian(871264, 161);
+
+ #
+ # Firmware 45, type: STD FW MTS (0x00000004), id: NTSC PAL/M PAL/N (000000000000b700), size: 169
+ #
+
+ write_le32(0x00000004); # Type
+ write_le64(0x00000000, 0x0000b700); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(871936, 169);
+
+ #
+ # Firmware 46, type: STD FW MTS LCD (0x00001004), id: NTSC PAL/M PAL/N (000000000000b700), size: 169
+ #
+
+ write_le32(0x00001004); # Type
+ write_le64(0x00000000, 0x0000b700); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(872112, 169);
+
+ #
+ # Firmware 47, type: STD FW MTS LCD NOGD (0x00003004), id: NTSC PAL/M PAL/N (000000000000b700), size: 169
+ #
+
+ write_le32(0x00003004); # Type
+ write_le64(0x00000000, 0x0000b700); # ID
+ write_le32(169); # Size
+ write_hunk_fix_endian(872288, 169);
+
+ #
+ # Firmware 48, type: SCODE FW HAS IF (0x60000000), IF = 3.28 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(3280); # IF
+ write_le32(192); # Size
+ write_hunk(811896, 192);
+
+ #
+ # Firmware 49, type: SCODE FW HAS IF (0x60000000), IF = 3.30 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(3300); # IF
+ write_le32(192); # Size
+ write_hunk(813048, 192);
+
+ #
+ # Firmware 50, type: SCODE FW HAS IF (0x60000000), IF = 3.44 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(3440); # IF
+ write_le32(192); # Size
+ write_hunk(812280, 192);
+
+ #
+ # Firmware 51, type: SCODE FW HAS IF (0x60000000), IF = 3.46 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(3460); # IF
+ write_le32(192); # Size
+ write_hunk(812472, 192);
+
+ #
+ # Firmware 52, type: SCODE FW DTV6 ATSC OREN36 HAS IF (0x60210020), IF = 3.80 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60210020); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(3800); # IF
+ write_le32(192); # Size
+ write_hunk(809784, 192);
+
+ #
+ # Firmware 53, type: SCODE FW HAS IF (0x60000000), IF = 4.00 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(4000); # IF
+ write_le32(192); # Size
+ write_hunk(812088, 192);
+
+ #
+ # Firmware 54, type: SCODE FW DTV6 ATSC TOYOTA388 HAS IF (0x60410020), IF = 4.08 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60410020); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(4080); # IF
+ write_le32(192); # Size
+ write_hunk(809976, 192);
+
+ #
+ # Firmware 55, type: SCODE FW HAS IF (0x60000000), IF = 4.20 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(4200); # IF
+ write_le32(192); # Size
+ write_hunk(811704, 192);
+
+ #
+ # Firmware 56, type: SCODE FW MONO HAS IF (0x60008000), IF = 4.32 MHz id: NTSC/M Kr (0000000000008000), size: 192
+ #
+
+ write_le32(0x60008000); # Type
+ write_le64(0x00000000, 0x00008000); # ID
+ write_le16(4320); # IF
+ write_le32(192); # Size
+ write_hunk(808056, 192);
+
+ #
+ # Firmware 57, type: SCODE FW HAS IF (0x60000000), IF = 4.45 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(4450); # IF
+ write_le32(192); # Size
+ write_hunk(812664, 192);
+
+ #
+ # Firmware 58, type: SCODE FW HAS IF (0x60000000), IF = 4.50 MHz id: NTSC/M Jp (0000000000002000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00002000); # ID
+ write_le16(4500); # IF
+ write_le32(192); # Size
+ write_hunk(807672, 192);
+
+ #
+ # Firmware 59, type: SCODE FW LCD NOGD IF HAS IF (0x60023000), IF = 4.60 MHz id: NTSC/M Kr (0000000000008000), size: 192
+ #
+
+ write_le32(0x60023000); # Type
+ write_le64(0x00000000, 0x00008000); # ID
+ write_le16(4600); # IF
+ write_le32(192); # Size
+ write_hunk(807864, 192);
+
+ #
+ # Firmware 60, type: SCODE FW DTV78 ZARLINK456 HAS IF (0x62000100), IF = 4.76 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x62000100); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(4760); # IF
+ write_le32(192); # Size
+ write_hunk(807288, 192);
+
+ #
+ # Firmware 61, type: SCODE FW HAS IF (0x60000000), IF = 4.94 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(4940); # IF
+ write_le32(192); # Size
+ write_hunk(811512, 192);
+
+ #
+ # Firmware 62, type: SCODE FW DTV7 ZARLINK456 HAS IF (0x62000080), IF = 5.26 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x62000080); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(5260); # IF
+ write_le32(192); # Size
+ write_hunk(810552, 192);
+
+ #
+ # Firmware 63, type: SCODE FW MONO HAS IF (0x60008000), IF = 5.32 MHz id: PAL/BG NICAM/B (0000000800000007), size: 192
+ #
+
+ write_le32(0x60008000); # Type
+ write_le64(0x00000008, 0x00000007); # ID
+ write_le16(5320); # IF
+ write_le32(192); # Size
+ write_hunk(810744, 192);
+
+ #
+ # Firmware 64, type: SCODE FW DTV8 CHINA HAS IF (0x64000200), IF = 5.40 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x64000200); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(5400); # IF
+ write_le32(192); # Size
+ write_hunk(807096, 192);
+
+ #
+ # Firmware 65, type: SCODE FW DTV6 ATSC OREN538 HAS IF (0x60110020), IF = 5.58 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60110020); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(5580); # IF
+ write_le32(192); # Size
+ write_hunk(809592, 192);
+
+ #
+ # Firmware 66, type: SCODE FW HAS IF (0x60000000), IF = 5.64 MHz id: PAL/BG A2/B (0000000200000007), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000002, 0x00000007); # ID
+ write_le16(5640); # IF
+ write_le32(192); # Size
+ write_hunk(808440, 192);
+
+ #
+ # Firmware 67, type: SCODE FW HAS IF (0x60000000), IF = 5.74 MHz id: PAL/BG NICAM/B (0000000800000007), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000008, 0x00000007); # ID
+ write_le16(5740); # IF
+ write_le32(192); # Size
+ write_hunk(808632, 192);
+
+ #
+ # Firmware 68, type: SCODE FW DTV7 DIBCOM52 HAS IF (0x61000080), IF = 5.90 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x61000080); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(5900); # IF
+ write_le32(192); # Size
+ write_hunk(810360, 192);
+
+ #
+ # Firmware 69, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.00 MHz id: PAL/I (0000000000000010), size: 192
+ #
+
+ write_le32(0x60008000); # Type
+ write_le64(0x00000000, 0x00000010); # ID
+ write_le16(6000); # IF
+ write_le32(192); # Size
+ write_hunk(808824, 192);
+
+ #
+ # Firmware 70, type: SCODE FW DTV6 QAM F6MHZ HAS IF (0x68000060), IF = 6.20 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x68000060); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(6200); # IF
+ write_le32(192); # Size
+ write_hunk(809400, 192);
+
+ #
+ # Firmware 71, type: SCODE FW HAS IF (0x60000000), IF = 6.24 MHz id: PAL/I (0000000000000010), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000010); # ID
+ write_le16(6240); # IF
+ write_le32(192); # Size
+ write_hunk(808248, 192);
+
+ #
+ # Firmware 72, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.32 MHz id: SECAM/K1 (0000000000200000), size: 192
+ #
+
+ write_le32(0x60008000); # Type
+ write_le64(0x00000000, 0x00200000); # ID
+ write_le16(6320); # IF
+ write_le32(192); # Size
+ write_hunk(811320, 192);
+
+ #
+ # Firmware 73, type: SCODE FW HAS IF (0x60000000), IF = 6.34 MHz id: SECAM/K1 (0000000000200000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00200000); # ID
+ write_le16(6340); # IF
+ write_le32(192); # Size
+ write_hunk(809208, 192);
+
+ #
+ # Firmware 74, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.50 MHz id: SECAM/K3 (0000000004000000), size: 192
+ #
+
+ write_le32(0x60008000); # Type
+ write_le64(0x00000000, 0x04000000); # ID
+ write_le16(6500); # IF
+ write_le32(192); # Size
+ write_hunk(811128, 192);
+
+ #
+ # Firmware 75, type: SCODE FW DTV6 ATSC ATI638 HAS IF (0x60090020), IF = 6.58 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60090020); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(6580); # IF
+ write_le32(192); # Size
+ write_hunk(807480, 192);
+
+ #
+ # Firmware 76, type: SCODE FW HAS IF (0x60000000), IF = 6.60 MHz id: PAL/DK A2 (00000003000000e0), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000003, 0x000000e0); # ID
+ write_le16(6600); # IF
+ write_le32(192); # Size
+ write_hunk(809016, 192);
+
+ #
+ # Firmware 77, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.68 MHz id: PAL/DK A2 (00000003000000e0), size: 192
+ #
+
+ write_le32(0x60008000); # Type
+ write_le64(0x00000003, 0x000000e0); # ID
+ write_le16(6680); # IF
+ write_le32(192); # Size
+ write_hunk(810936, 192);
+
+ #
+ # Firmware 78, type: SCODE FW DTV6 ATSC TOYOTA794 HAS IF (0x60810020), IF = 8.14 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60810020); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(8140); # IF
+ write_le32(192); # Size
+ write_hunk(810168, 192);
+
+ #
+ # Firmware 79, type: SCODE FW HAS IF (0x60000000), IF = 8.20 MHz id: (0000000000000000), size: 192
+ #
+
+ write_le32(0x60000000); # Type
+ write_le64(0x00000000, 0x00000000); # ID
+ write_le16(8200); # IF
+ write_le32(192); # Size
+ write_hunk(812856, 192);
+}
+
+sub extract_firmware {
+ my $sourcefile = "hcw85bda.sys";
+ my $hash = "0e44dbf63bb0169d57446aec21881ff2";
+ my $outfile = "xc3028-v27.fw";
+ my $name = "xc2028 firmware";
+ my $version = 519;
+ my $nr_desc = 80;
+ my $out;
+
+ verify($sourcefile, $hash);
+
+ open INFILE, "<$sourcefile";
+ main_firmware($outfile, $name, $version, $nr_desc);
+ close INFILE;
+}
+
+extract_firmware;
+printf "Firmwares generated.\n";
diff --git a/trunk/Documentation/video4linux/sn9c102.txt b/trunk/Documentation/video4linux/sn9c102.txt
index 1ffad19ce891..b26f5195af51 100644
--- a/trunk/Documentation/video4linux/sn9c102.txt
+++ b/trunk/Documentation/video4linux/sn9c102.txt
@@ -568,6 +568,7 @@ the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'.
Many thanks to following persons for their contribute (listed in alphabetical
order):
+- David Anderson for the donation of a webcam;
- Luca Capello for the donation of a webcam;
- Philippe Coval for having helped testing the PAS202BCA image sensor;
- Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the
diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS
index 2340cfb1e25d..17524afa7475 100644
--- a/trunk/MAINTAINERS
+++ b/trunk/MAINTAINERS
@@ -2141,6 +2141,15 @@ L: isdn4linux@listserv.isdn4linux.de
W: http://www.melware.de
S: Maintained
+IVTV VIDEO4LINUX DRIVER
+P: Hans Verkuil
+M: hverkuil@xs4all.nl
+L: ivtv-devel@ivtvdriver.org
+L: ivtv-users@ivtvdriver.org
+L: video4linux-list@redhat.com
+W: http://www.ivtvdriver.org
+S: Maintained
+
JOURNALLING FLASH FILE SYSTEM V2 (JFFS2)
P: David Woodhouse
M: dwmw2@infradead.org
diff --git a/trunk/arch/arm/kernel/time.c b/trunk/arch/arm/kernel/time.c
index f6f3689a86ee..e59b5b84168d 100644
--- a/trunk/arch/arm/kernel/time.c
+++ b/trunk/arch/arm/kernel/time.c
@@ -79,17 +79,6 @@ static unsigned long dummy_gettimeoffset(void)
}
#endif
-/*
- * An implementation of printk_clock() independent from
- * sched_clock(). This avoids non-bootable kernels when
- * printk_clock is enabled.
- */
-unsigned long long printk_clock(void)
-{
- return (unsigned long long)(jiffies - INITIAL_JIFFIES) *
- (1000000000 / HZ);
-}
-
static unsigned long next_rtc_update;
/*
diff --git a/trunk/arch/ia64/kernel/setup.c b/trunk/arch/ia64/kernel/setup.c
index 4ac2b1f1bd3b..86028c69861e 100644
--- a/trunk/arch/ia64/kernel/setup.c
+++ b/trunk/arch/ia64/kernel/setup.c
@@ -71,8 +71,6 @@ unsigned long __per_cpu_offset[NR_CPUS];
EXPORT_SYMBOL(__per_cpu_offset);
#endif
-extern void ia64_setup_printk_clock(void);
-
DEFINE_PER_CPU(struct cpuinfo_ia64, cpu_info);
DEFINE_PER_CPU(unsigned long, local_per_cpu_offset);
unsigned long ia64_cycles_per_usec;
@@ -507,8 +505,6 @@ setup_arch (char **cmdline_p)
/* process SAL system table: */
ia64_sal_init(__va(efi.sal_systab));
- ia64_setup_printk_clock();
-
#ifdef CONFIG_SMP
cpu_physical_id(0) = hard_smp_processor_id();
#endif
diff --git a/trunk/arch/ia64/kernel/time.c b/trunk/arch/ia64/kernel/time.c
index 2bb84214e5f1..3ab042720970 100644
--- a/trunk/arch/ia64/kernel/time.c
+++ b/trunk/arch/ia64/kernel/time.c
@@ -344,33 +344,6 @@ udelay (unsigned long usecs)
}
EXPORT_SYMBOL(udelay);
-static unsigned long long ia64_itc_printk_clock(void)
-{
- if (ia64_get_kr(IA64_KR_PER_CPU_DATA))
- return sched_clock();
- return 0;
-}
-
-static unsigned long long ia64_default_printk_clock(void)
-{
- return (unsigned long long)(jiffies_64 - INITIAL_JIFFIES) *
- (1000000000/HZ);
-}
-
-unsigned long long (*ia64_printk_clock)(void) = &ia64_default_printk_clock;
-
-unsigned long long printk_clock(void)
-{
- return ia64_printk_clock();
-}
-
-void __init
-ia64_setup_printk_clock(void)
-{
- if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT))
- ia64_printk_clock = ia64_itc_printk_clock;
-}
-
/* IA64 doesn't cache the timezone */
void update_vsyscall_tz(void)
{
diff --git a/trunk/arch/ia64/sn/kernel/setup.c b/trunk/arch/ia64/sn/kernel/setup.c
index 1f38a3a68390..bb1d24929640 100644
--- a/trunk/arch/ia64/sn/kernel/setup.c
+++ b/trunk/arch/ia64/sn/kernel/setup.c
@@ -64,7 +64,6 @@ extern void sn_timer_init(void);
extern unsigned long last_time_offset;
extern void (*ia64_mark_idle) (int);
extern void snidle(int);
-extern unsigned long long (*ia64_printk_clock)(void);
unsigned long sn_rtc_cycles_per_second;
EXPORT_SYMBOL(sn_rtc_cycles_per_second);
@@ -360,14 +359,6 @@ sn_scan_pcdp(void)
static unsigned long sn2_rtc_initial;
-static unsigned long long ia64_sn2_printk_clock(void)
-{
- unsigned long rtc_now = rtc_time();
-
- return (rtc_now - sn2_rtc_initial) *
- (1000000000 / sn_rtc_cycles_per_second);
-}
-
/**
* sn_setup - SN platform setup routine
* @cmdline_p: kernel command line
@@ -468,8 +459,6 @@ void __init sn_setup(char **cmdline_p)
platform_intr_list[ACPI_INTERRUPT_CPEI] = IA64_CPE_VECTOR;
- ia64_printk_clock = ia64_sn2_printk_clock;
-
printk("SGI SAL version %x.%02x\n", version >> 8, version & 0x00FF);
/*
diff --git a/trunk/arch/mips/kernel/mips-mt-fpaff.c b/trunk/arch/mips/kernel/mips-mt-fpaff.c
index 892665bb12b1..bb4f00c0cbe9 100644
--- a/trunk/arch/mips/kernel/mips-mt-fpaff.c
+++ b/trunk/arch/mips/kernel/mips-mt-fpaff.c
@@ -58,13 +58,13 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask)))
return -EFAULT;
- lock_cpu_hotplug();
+ get_online_cpus();
read_lock(&tasklist_lock);
p = find_process_by_pid(pid);
if (!p) {
read_unlock(&tasklist_lock);
- unlock_cpu_hotplug();
+ put_online_cpus();
return -ESRCH;
}
@@ -106,7 +106,7 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
out_unlock:
put_task_struct(p);
- unlock_cpu_hotplug();
+ put_online_cpus();
return retval;
}
@@ -125,7 +125,7 @@ asmlinkage long mipsmt_sys_sched_getaffinity(pid_t pid, unsigned int len,
if (len < real_len)
return -EINVAL;
- lock_cpu_hotplug();
+ get_online_cpus();
read_lock(&tasklist_lock);
retval = -ESRCH;
@@ -140,7 +140,7 @@ asmlinkage long mipsmt_sys_sched_getaffinity(pid_t pid, unsigned int len,
out_unlock:
read_unlock(&tasklist_lock);
- unlock_cpu_hotplug();
+ put_online_cpus();
if (retval)
return retval;
if (copy_to_user(user_mask_ptr, &mask, real_len))
diff --git a/trunk/arch/powerpc/platforms/pseries/hotplug-cpu.c b/trunk/arch/powerpc/platforms/pseries/hotplug-cpu.c
index 412e6b42986f..c4ad54e0f288 100644
--- a/trunk/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/trunk/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -153,7 +153,7 @@ static int pseries_add_processor(struct device_node *np)
for (i = 0; i < nthreads; i++)
cpu_set(i, tmp);
- lock_cpu_hotplug();
+ cpu_maps_update_begin();
BUG_ON(!cpus_subset(cpu_present_map, cpu_possible_map));
@@ -190,7 +190,7 @@ static int pseries_add_processor(struct device_node *np)
}
err = 0;
out_unlock:
- unlock_cpu_hotplug();
+ cpu_maps_update_done();
return err;
}
@@ -211,7 +211,7 @@ static void pseries_remove_processor(struct device_node *np)
nthreads = len / sizeof(u32);
- lock_cpu_hotplug();
+ cpu_maps_update_begin();
for (i = 0; i < nthreads; i++) {
for_each_present_cpu(cpu) {
if (get_hard_smp_processor_id(cpu) != intserv[i])
@@ -225,7 +225,7 @@ static void pseries_remove_processor(struct device_node *np)
printk(KERN_WARNING "Could not find cpu to remove "
"with physical id 0x%x\n", intserv[i]);
}
- unlock_cpu_hotplug();
+ cpu_maps_update_done();
}
static int pseries_smp_notifier(struct notifier_block *nb,
diff --git a/trunk/arch/powerpc/platforms/pseries/rtasd.c b/trunk/arch/powerpc/platforms/pseries/rtasd.c
index 73401c820110..e3078ce41518 100644
--- a/trunk/arch/powerpc/platforms/pseries/rtasd.c
+++ b/trunk/arch/powerpc/platforms/pseries/rtasd.c
@@ -382,7 +382,7 @@ static void do_event_scan_all_cpus(long delay)
{
int cpu;
- lock_cpu_hotplug();
+ get_online_cpus();
cpu = first_cpu(cpu_online_map);
for (;;) {
set_cpus_allowed(current, cpumask_of_cpu(cpu));
@@ -390,15 +390,15 @@ static void do_event_scan_all_cpus(long delay)
set_cpus_allowed(current, CPU_MASK_ALL);
/* Drop hotplug lock, and sleep for the specified delay */
- unlock_cpu_hotplug();
+ put_online_cpus();
msleep_interruptible(delay);
- lock_cpu_hotplug();
+ get_online_cpus();
cpu = next_cpu(cpu, cpu_online_map);
if (cpu == NR_CPUS)
break;
}
- unlock_cpu_hotplug();
+ put_online_cpus();
}
static int rtasd(void *unused)
diff --git a/trunk/arch/x86/kernel/cpu/mtrr/main.c b/trunk/arch/x86/kernel/cpu/mtrr/main.c
index 3b20613325dc..beb45c9c0835 100644
--- a/trunk/arch/x86/kernel/cpu/mtrr/main.c
+++ b/trunk/arch/x86/kernel/cpu/mtrr/main.c
@@ -349,7 +349,7 @@ int mtrr_add_page(unsigned long base, unsigned long size,
replace = -1;
/* No CPU hotplug when we change MTRR entries */
- lock_cpu_hotplug();
+ get_online_cpus();
/* Search for existing MTRR */
mutex_lock(&mtrr_mutex);
for (i = 0; i < num_var_ranges; ++i) {
@@ -405,7 +405,7 @@ int mtrr_add_page(unsigned long base, unsigned long size,
error = i;
out:
mutex_unlock(&mtrr_mutex);
- unlock_cpu_hotplug();
+ put_online_cpus();
return error;
}
@@ -495,7 +495,7 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
max = num_var_ranges;
/* No CPU hotplug when we change MTRR entries */
- lock_cpu_hotplug();
+ get_online_cpus();
mutex_lock(&mtrr_mutex);
if (reg < 0) {
/* Search for existing MTRR */
@@ -536,7 +536,7 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
error = reg;
out:
mutex_unlock(&mtrr_mutex);
- unlock_cpu_hotplug();
+ put_online_cpus();
return error;
}
/**
diff --git a/trunk/arch/x86/kernel/entry_64.S b/trunk/arch/x86/kernel/entry_64.S
index 3a058bb16409..e70f3881d7e4 100644
--- a/trunk/arch/x86/kernel/entry_64.S
+++ b/trunk/arch/x86/kernel/entry_64.S
@@ -283,7 +283,7 @@ sysret_careful:
sysret_signal:
TRACE_IRQS_ON
sti
- testl $(_TIF_SIGPENDING|_TIF_SINGLESTEP|_TIF_MCE_NOTIFY),%edx
+ testl $_TIF_DO_NOTIFY_MASK,%edx
jz 1f
/* Really a signal */
@@ -377,7 +377,7 @@ int_very_careful:
jmp int_restore_rest
int_signal:
- testl $(_TIF_SIGPENDING|_TIF_SINGLESTEP|_TIF_MCE_NOTIFY),%edx
+ testl $_TIF_DO_NOTIFY_MASK,%edx
jz 1f
movq %rsp,%rdi # &ptregs -> arg1
xorl %esi,%esi # oldset -> arg2
@@ -603,7 +603,7 @@ retint_careful:
jmp retint_check
retint_signal:
- testl $(_TIF_SIGPENDING|_TIF_SINGLESTEP|_TIF_MCE_NOTIFY),%edx
+ testl $_TIF_DO_NOTIFY_MASK,%edx
jz retint_swapgs
TRACE_IRQS_ON
sti
diff --git a/trunk/arch/x86/kernel/microcode.c b/trunk/arch/x86/kernel/microcode.c
index 09c315214a5e..40cfd5488719 100644
--- a/trunk/arch/x86/kernel/microcode.c
+++ b/trunk/arch/x86/kernel/microcode.c
@@ -436,7 +436,7 @@ static ssize_t microcode_write (struct file *file, const char __user *buf, size_
return -EINVAL;
}
- lock_cpu_hotplug();
+ get_online_cpus();
mutex_lock(µcode_mutex);
user_buffer = (void __user *) buf;
@@ -447,7 +447,7 @@ static ssize_t microcode_write (struct file *file, const char __user *buf, size_
ret = (ssize_t)len;
mutex_unlock(µcode_mutex);
- unlock_cpu_hotplug();
+ put_online_cpus();
return ret;
}
@@ -658,14 +658,14 @@ static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz)
old = current->cpus_allowed;
- lock_cpu_hotplug();
+ get_online_cpus();
set_cpus_allowed(current, cpumask_of_cpu(cpu));
mutex_lock(µcode_mutex);
if (uci->valid)
err = cpu_request_microcode(cpu);
mutex_unlock(µcode_mutex);
- unlock_cpu_hotplug();
+ put_online_cpus();
set_cpus_allowed(current, old);
}
if (err)
@@ -817,9 +817,9 @@ static int __init microcode_init (void)
return PTR_ERR(microcode_pdev);
}
- lock_cpu_hotplug();
+ get_online_cpus();
error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver);
- unlock_cpu_hotplug();
+ put_online_cpus();
if (error) {
microcode_dev_exit();
platform_device_unregister(microcode_pdev);
@@ -839,9 +839,9 @@ static void __exit microcode_exit (void)
unregister_hotcpu_notifier(&mc_cpu_notifier);
- lock_cpu_hotplug();
+ get_online_cpus();
sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver);
- unlock_cpu_hotplug();
+ put_online_cpus();
platform_device_unregister(microcode_pdev);
}
diff --git a/trunk/arch/x86/kernel/signal_32.c b/trunk/arch/x86/kernel/signal_32.c
index 9bdd83022f5f..20f29e4c1d33 100644
--- a/trunk/arch/x86/kernel/signal_32.c
+++ b/trunk/arch/x86/kernel/signal_32.c
@@ -658,6 +658,9 @@ void do_notify_resume(struct pt_regs *regs, void *_unused,
/* deal with pending signal delivery */
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
do_signal(regs);
+
+ if (thread_info_flags & _TIF_HRTICK_RESCHED)
+ hrtick_resched();
clear_thread_flag(TIF_IRET);
}
diff --git a/trunk/arch/x86/kernel/signal_64.c b/trunk/arch/x86/kernel/signal_64.c
index ab086b0357fc..38d806467c0f 100644
--- a/trunk/arch/x86/kernel/signal_64.c
+++ b/trunk/arch/x86/kernel/signal_64.c
@@ -480,6 +480,9 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
/* deal with pending signal delivery */
if (thread_info_flags & (_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK))
do_signal(regs);
+
+ if (thread_info_flags & _TIF_HRTICK_RESCHED)
+ hrtick_resched();
}
void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
diff --git a/trunk/arch/x86/kernel/stacktrace.c b/trunk/arch/x86/kernel/stacktrace.c
index 6fa6cf036c70..55771fd7e545 100644
--- a/trunk/arch/x86/kernel/stacktrace.c
+++ b/trunk/arch/x86/kernel/stacktrace.c
@@ -33,6 +33,19 @@ static void save_stack_address(void *data, unsigned long addr)
trace->entries[trace->nr_entries++] = addr;
}
+static void save_stack_address_nosched(void *data, unsigned long addr)
+{
+ struct stack_trace *trace = (struct stack_trace *)data;
+ if (in_sched_functions(addr))
+ return;
+ if (trace->skip > 0) {
+ trace->skip--;
+ return;
+ }
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = addr;
+}
+
static const struct stacktrace_ops save_stack_ops = {
.warning = save_stack_warning,
.warning_symbol = save_stack_warning_symbol,
@@ -40,6 +53,13 @@ static const struct stacktrace_ops save_stack_ops = {
.address = save_stack_address,
};
+static const struct stacktrace_ops save_stack_ops_nosched = {
+ .warning = save_stack_warning,
+ .warning_symbol = save_stack_warning_symbol,
+ .stack = save_stack_stack,
+ .address = save_stack_address_nosched,
+};
+
/*
* Save stack-backtrace addresses into a stack_trace buffer.
*/
@@ -50,3 +70,10 @@ void save_stack_trace(struct stack_trace *trace)
trace->entries[trace->nr_entries++] = ULONG_MAX;
}
EXPORT_SYMBOL(save_stack_trace);
+
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ dump_trace(tsk, NULL, NULL, &save_stack_ops_nosched, trace);
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
diff --git a/trunk/drivers/ata/Kconfig b/trunk/drivers/ata/Kconfig
index ba63619ae5df..2478cca653de 100644
--- a/trunk/drivers/ata/Kconfig
+++ b/trunk/drivers/ata/Kconfig
@@ -459,6 +459,15 @@ config PATA_NETCELL
If unsure, say N.
+config PATA_NINJA32
+ tristate "Ninja32/Delkin Cardbus ATA support (Experimental)"
+ depends on PCI && EXPERIMENTAL
+ help
+ This option enables support for the Ninja32, Delkin and
+ possibly other brands of Cardbus ATA adapter
+
+ If unsure, say N.
+
config PATA_NS87410
tristate "Nat Semi NS87410 PATA support (Experimental)"
depends on PCI && EXPERIMENTAL
diff --git a/trunk/drivers/ata/Makefile b/trunk/drivers/ata/Makefile
index b13feb2c5dae..82550c16818c 100644
--- a/trunk/drivers/ata/Makefile
+++ b/trunk/drivers/ata/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_PATA_IT821X) += pata_it821x.o
obj-$(CONFIG_PATA_IT8213) += pata_it8213.o
obj-$(CONFIG_PATA_JMICRON) += pata_jmicron.o
obj-$(CONFIG_PATA_NETCELL) += pata_netcell.o
+obj-$(CONFIG_PATA_NINJA32) += pata_ninja32.o
obj-$(CONFIG_PATA_NS87410) += pata_ns87410.o
obj-$(CONFIG_PATA_NS87415) += pata_ns87415.o
obj-$(CONFIG_PATA_OPTI) += pata_opti.o
diff --git a/trunk/drivers/ata/ahci.c b/trunk/drivers/ata/ahci.c
index 54f38c21dd95..6f089b899a1a 100644
--- a/trunk/drivers/ata/ahci.c
+++ b/trunk/drivers/ata/ahci.c
@@ -198,18 +198,18 @@ enum {
};
struct ahci_cmd_hdr {
- u32 opts;
- u32 status;
- u32 tbl_addr;
- u32 tbl_addr_hi;
- u32 reserved[4];
+ __le32 opts;
+ __le32 status;
+ __le32 tbl_addr;
+ __le32 tbl_addr_hi;
+ __le32 reserved[4];
};
struct ahci_sg {
- u32 addr;
- u32 addr_hi;
- u32 reserved;
- u32 flags_size;
+ __le32 addr;
+ __le32 addr_hi;
+ __le32 reserved;
+ __le32 flags_size;
};
struct ahci_host_priv {
@@ -597,6 +597,20 @@ static inline void __iomem *ahci_port_base(struct ata_port *ap)
return __ahci_port_base(ap->host, ap->port_no);
}
+static void ahci_enable_ahci(void __iomem *mmio)
+{
+ u32 tmp;
+
+ /* turn on AHCI_EN */
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_AHCI_EN)) {
+ tmp |= HOST_AHCI_EN;
+ writel(tmp, mmio + HOST_CTL);
+ tmp = readl(mmio + HOST_CTL); /* flush && sanity check */
+ WARN_ON(!(tmp & HOST_AHCI_EN));
+ }
+}
+
/**
* ahci_save_initial_config - Save and fixup initial config values
* @pdev: target PCI device
@@ -619,6 +633,9 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
u32 cap, port_map;
int i;
+ /* make sure AHCI mode is enabled before accessing CAP */
+ ahci_enable_ahci(mmio);
+
/* Values prefixed with saved_ are written back to host after
* reset. Values without are used for driver operation.
*/
@@ -1036,19 +1053,17 @@ static int ahci_deinit_port(struct ata_port *ap, const char **emsg)
static int ahci_reset_controller(struct ata_host *host)
{
struct pci_dev *pdev = to_pci_dev(host->dev);
+ struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
u32 tmp;
/* we must be in AHCI mode, before using anything
* AHCI-specific, such as HOST_RESET.
*/
- tmp = readl(mmio + HOST_CTL);
- if (!(tmp & HOST_AHCI_EN)) {
- tmp |= HOST_AHCI_EN;
- writel(tmp, mmio + HOST_CTL);
- }
+ ahci_enable_ahci(mmio);
/* global controller reset */
+ tmp = readl(mmio + HOST_CTL);
if ((tmp & HOST_RESET) == 0) {
writel(tmp | HOST_RESET, mmio + HOST_CTL);
readl(mmio + HOST_CTL); /* flush */
@@ -1067,8 +1082,7 @@ static int ahci_reset_controller(struct ata_host *host)
}
/* turn on AHCI mode */
- writel(HOST_AHCI_EN, mmio + HOST_CTL);
- (void) readl(mmio + HOST_CTL); /* flush */
+ ahci_enable_ahci(mmio);
/* some registers might be cleared on reset. restore initial values */
ahci_restore_initial_config(host);
@@ -1078,8 +1092,10 @@ static int ahci_reset_controller(struct ata_host *host)
/* configure PCS */
pci_read_config_word(pdev, 0x92, &tmp16);
- tmp16 |= 0xf;
- pci_write_config_word(pdev, 0x92, tmp16);
+ if ((tmp16 & hpriv->port_map) != hpriv->port_map) {
+ tmp16 |= hpriv->port_map;
+ pci_write_config_word(pdev, 0x92, tmp16);
+ }
}
return 0;
@@ -1480,35 +1496,31 @@ static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl)
{
struct scatterlist *sg;
- struct ahci_sg *ahci_sg;
- unsigned int n_sg = 0;
+ struct ahci_sg *ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
+ unsigned int si;
VPRINTK("ENTER\n");
/*
* Next, the S/G list.
*/
- ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
dma_addr_t addr = sg_dma_address(sg);
u32 sg_len = sg_dma_len(sg);
- ahci_sg->addr = cpu_to_le32(addr & 0xffffffff);
- ahci_sg->addr_hi = cpu_to_le32((addr >> 16) >> 16);
- ahci_sg->flags_size = cpu_to_le32(sg_len - 1);
-
- ahci_sg++;
- n_sg++;
+ ahci_sg[si].addr = cpu_to_le32(addr & 0xffffffff);
+ ahci_sg[si].addr_hi = cpu_to_le32((addr >> 16) >> 16);
+ ahci_sg[si].flags_size = cpu_to_le32(sg_len - 1);
}
- return n_sg;
+ return si;
}
static void ahci_qc_prep(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ahci_port_priv *pp = ap->private_data;
- int is_atapi = is_atapi_taskfile(&qc->tf);
+ int is_atapi = ata_is_atapi(qc->tf.protocol);
void *cmd_tbl;
u32 opts;
const u32 cmd_fis_len = 5; /* five dwords */
diff --git a/trunk/drivers/ata/ata_generic.c b/trunk/drivers/ata/ata_generic.c
index 90329982bef7..20534202fc79 100644
--- a/trunk/drivers/ata/ata_generic.c
+++ b/trunk/drivers/ata/ata_generic.c
@@ -26,7 +26,7 @@
#include
#define DRV_NAME "ata_generic"
-#define DRV_VERSION "0.2.13"
+#define DRV_VERSION "0.2.15"
/*
* A generic parallel ATA driver using libata
@@ -48,27 +48,47 @@ static int generic_set_mode(struct ata_link *link, struct ata_device **unused)
struct ata_port *ap = link->ap;
int dma_enabled = 0;
struct ata_device *dev;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
/* Bits 5 and 6 indicate if DMA is active on master/slave */
if (ap->ioaddr.bmdma_addr)
dma_enabled = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+ if (pdev->vendor == PCI_VENDOR_ID_CENATEK)
+ dma_enabled = 0xFF;
+
ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- /* We don't really care */
- dev->pio_mode = XFER_PIO_0;
- dev->dma_mode = XFER_MW_DMA_0;
- /* We do need the right mode information for DMA or PIO
- and this comes from the current configuration flags */
- if (dma_enabled & (1 << (5 + dev->devno))) {
- ata_id_to_dma_mode(dev, XFER_MW_DMA_0);
- dev->flags &= ~ATA_DFLAG_PIO;
- } else {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
+ if (!ata_dev_enabled(dev))
+ continue;
+
+ /* We don't really care */
+ dev->pio_mode = XFER_PIO_0;
+ dev->dma_mode = XFER_MW_DMA_0;
+ /* We do need the right mode information for DMA or PIO
+ and this comes from the current configuration flags */
+ if (dma_enabled & (1 << (5 + dev->devno))) {
+ unsigned int xfer_mask = ata_id_xfermask(dev->id);
+ const char *name;
+
+ if (xfer_mask & (ATA_MASK_MWDMA | ATA_MASK_UDMA))
+ name = ata_mode_string(xfer_mask);
+ else {
+ /* SWDMA perhaps? */
+ name = "DMA";
+ xfer_mask |= ata_xfer_mode2mask(XFER_MW_DMA_0);
}
+
+ ata_dev_printk(dev, KERN_INFO, "configured for %s\n",
+ name);
+
+ dev->xfer_mode = ata_xfer_mask2mode(xfer_mask);
+ dev->xfer_shift = ata_xfer_mode2shift(dev->xfer_mode);
+ dev->flags &= ~ATA_DFLAG_PIO;
+ } else {
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
}
}
return 0;
@@ -185,6 +205,7 @@ static struct pci_device_id ata_generic[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE), },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561), },
{ PCI_DEVICE(PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558), },
+ { PCI_DEVICE(PCI_VENDOR_ID_CENATEK,PCI_DEVICE_ID_CENATEK_IDE), },
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO), },
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), },
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), },
diff --git a/trunk/drivers/ata/ata_piix.c b/trunk/drivers/ata/ata_piix.c
index b406b39b878e..a65c8ae5c461 100644
--- a/trunk/drivers/ata/ata_piix.c
+++ b/trunk/drivers/ata/ata_piix.c
@@ -101,39 +101,21 @@ enum {
ICH5_PMR = 0x90, /* port mapping register */
ICH5_PCS = 0x92, /* port control and status */
PIIX_SCC = 0x0A, /* sub-class code register */
+ PIIX_SIDPR_BAR = 5,
+ PIIX_SIDPR_LEN = 16,
+ PIIX_SIDPR_IDX = 0,
+ PIIX_SIDPR_DATA = 4,
- PIIX_FLAG_SCR = (1 << 26), /* SCR available */
PIIX_FLAG_AHCI = (1 << 27), /* AHCI possible */
PIIX_FLAG_CHECKINTR = (1 << 28), /* make sure PCI INTx enabled */
+ PIIX_FLAG_SIDPR = (1 << 29), /* SATA idx/data pair regs */
PIIX_PATA_FLAGS = ATA_FLAG_SLAVE_POSS,
PIIX_SATA_FLAGS = ATA_FLAG_SATA | PIIX_FLAG_CHECKINTR,
- /* combined mode. if set, PATA is channel 0.
- * if clear, PATA is channel 1.
- */
- PIIX_PORT_ENABLED = (1 << 0),
- PIIX_PORT_PRESENT = (1 << 4),
-
PIIX_80C_PRI = (1 << 5) | (1 << 4),
PIIX_80C_SEC = (1 << 7) | (1 << 6),
- /* controller IDs */
- piix_pata_mwdma = 0, /* PIIX3 MWDMA only */
- piix_pata_33, /* PIIX4 at 33Mhz */
- ich_pata_33, /* ICH up to UDMA 33 only */
- ich_pata_66, /* ICH up to 66 Mhz */
- ich_pata_100, /* ICH up to UDMA 100 */
- ich5_sata,
- ich6_sata,
- ich6_sata_ahci,
- ich6m_sata_ahci,
- ich8_sata_ahci,
- ich8_2port_sata,
- ich8m_apple_sata_ahci, /* locks up on second port enable */
- tolapai_sata_ahci,
- piix_pata_vmw, /* PIIX4 for VMware, spurious DMA_ERR */
-
/* constants for mapping table */
P0 = 0, /* port 0 */
P1 = 1, /* port 1 */
@@ -149,6 +131,24 @@ enum {
PIIX_HOST_BROKEN_SUSPEND = (1 << 24),
};
+enum piix_controller_ids {
+ /* controller IDs */
+ piix_pata_mwdma, /* PIIX3 MWDMA only */
+ piix_pata_33, /* PIIX4 at 33Mhz */
+ ich_pata_33, /* ICH up to UDMA 33 only */
+ ich_pata_66, /* ICH up to 66 Mhz */
+ ich_pata_100, /* ICH up to UDMA 100 */
+ ich5_sata,
+ ich6_sata,
+ ich6_sata_ahci,
+ ich6m_sata_ahci,
+ ich8_sata_ahci,
+ ich8_2port_sata,
+ ich8m_apple_sata_ahci, /* locks up on second port enable */
+ tolapai_sata_ahci,
+ piix_pata_vmw, /* PIIX4 for VMware, spurious DMA_ERR */
+};
+
struct piix_map_db {
const u32 mask;
const u16 port_enable;
@@ -157,6 +157,7 @@ struct piix_map_db {
struct piix_host_priv {
const int *map;
+ void __iomem *sidpr;
};
static int piix_init_one(struct pci_dev *pdev,
@@ -167,6 +168,9 @@ static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev);
static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev);
static int ich_pata_cable_detect(struct ata_port *ap);
static u8 piix_vmw_bmdma_status(struct ata_port *ap);
+static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val);
+static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val);
+static void piix_sidpr_error_handler(struct ata_port *ap);
#ifdef CONFIG_PM
static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
static int piix_pci_device_resume(struct pci_dev *pdev);
@@ -321,7 +325,6 @@ static const struct ata_port_operations piix_pata_ops = {
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.cable_detect = ata_cable_40wire,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
@@ -353,7 +356,6 @@ static const struct ata_port_operations ich_pata_ops = {
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.cable_detect = ich_pata_cable_detect,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
@@ -380,7 +382,6 @@ static const struct ata_port_operations piix_sata_ops = {
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
@@ -419,6 +420,35 @@ static const struct ata_port_operations piix_vmw_ops = {
.port_start = ata_port_start,
};
+static const struct ata_port_operations piix_sidpr_sata_ops = {
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+ .data_xfer = ata_data_xfer,
+
+ .scr_read = piix_sidpr_scr_read,
+ .scr_write = piix_sidpr_scr_write,
+
+ .freeze = ata_bmdma_freeze,
+ .thaw = ata_bmdma_thaw,
+ .error_handler = piix_sidpr_error_handler,
+ .post_internal_cmd = ata_bmdma_post_internal_cmd,
+
+ .irq_clear = ata_bmdma_irq_clear,
+ .irq_on = ata_irq_on,
+
+ .port_start = ata_port_start,
+};
+
static const struct piix_map_db ich5_map_db = {
.mask = 0x7,
.port_enable = 0x3,
@@ -526,7 +556,6 @@ static const struct piix_map_db *piix_map_db_table[] = {
static struct ata_port_info piix_port_info[] = {
[piix_pata_mwdma] = /* PIIX3 MWDMA only */
{
- .sht = &piix_sht,
.flags = PIIX_PATA_FLAGS,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x06, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
@@ -535,7 +564,6 @@ static struct ata_port_info piix_port_info[] = {
[piix_pata_33] = /* PIIX4 at 33MHz */
{
- .sht = &piix_sht,
.flags = PIIX_PATA_FLAGS,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x06, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
@@ -545,7 +573,6 @@ static struct ata_port_info piix_port_info[] = {
[ich_pata_33] = /* ICH0 - ICH at 33Mhz*/
{
- .sht = &piix_sht,
.flags = PIIX_PATA_FLAGS,
.pio_mask = 0x1f, /* pio 0-4 */
.mwdma_mask = 0x06, /* Check: maybe 0x07 */
@@ -555,7 +582,6 @@ static struct ata_port_info piix_port_info[] = {
[ich_pata_66] = /* ICH controllers up to 66MHz */
{
- .sht = &piix_sht,
.flags = PIIX_PATA_FLAGS,
.pio_mask = 0x1f, /* pio 0-4 */
.mwdma_mask = 0x06, /* MWDMA0 is broken on chip */
@@ -565,7 +591,6 @@ static struct ata_port_info piix_port_info[] = {
[ich_pata_100] =
{
- .sht = &piix_sht,
.flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x06, /* mwdma1-2 */
@@ -575,7 +600,6 @@ static struct ata_port_info piix_port_info[] = {
[ich5_sata] =
{
- .sht = &piix_sht,
.flags = PIIX_SATA_FLAGS,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
@@ -585,8 +609,7 @@ static struct ata_port_info piix_port_info[] = {
[ich6_sata] =
{
- .sht = &piix_sht,
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR,
+ .flags = PIIX_SATA_FLAGS,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = ATA_UDMA6,
@@ -595,9 +618,7 @@ static struct ata_port_info piix_port_info[] = {
[ich6_sata_ahci] =
{
- .sht = &piix_sht,
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR |
- PIIX_FLAG_AHCI,
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = ATA_UDMA6,
@@ -606,9 +627,7 @@ static struct ata_port_info piix_port_info[] = {
[ich6m_sata_ahci] =
{
- .sht = &piix_sht,
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR |
- PIIX_FLAG_AHCI,
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = ATA_UDMA6,
@@ -617,9 +636,8 @@ static struct ata_port_info piix_port_info[] = {
[ich8_sata_ahci] =
{
- .sht = &piix_sht,
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR |
- PIIX_FLAG_AHCI,
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI |
+ PIIX_FLAG_SIDPR,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = ATA_UDMA6,
@@ -628,9 +646,8 @@ static struct ata_port_info piix_port_info[] = {
[ich8_2port_sata] =
{
- .sht = &piix_sht,
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR |
- PIIX_FLAG_AHCI,
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI |
+ PIIX_FLAG_SIDPR,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = ATA_UDMA6,
@@ -639,9 +656,7 @@ static struct ata_port_info piix_port_info[] = {
[tolapai_sata_ahci] =
{
- .sht = &piix_sht,
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR |
- PIIX_FLAG_AHCI,
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = ATA_UDMA6,
@@ -650,9 +665,8 @@ static struct ata_port_info piix_port_info[] = {
[ich8m_apple_sata_ahci] =
{
- .sht = &piix_sht,
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SCR |
- PIIX_FLAG_AHCI,
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI |
+ PIIX_FLAG_SIDPR,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = ATA_UDMA6,
@@ -1001,6 +1015,180 @@ static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev)
do_pata_set_dmamode(ap, adev, 1);
}
+/*
+ * Serial ATA Index/Data Pair Superset Registers access
+ *
+ * Beginning from ICH8, there's a sane way to access SCRs using index
+ * and data register pair located at BAR5. This creates an
+ * interesting problem of mapping two SCRs to one port.
+ *
+ * Although they have separate SCRs, the master and slave aren't
+ * independent enough to be treated as separate links - e.g. softreset
+ * resets both. Also, there's no protocol defined for hard resetting
+ * singled device sharing the virtual port (no defined way to acquire
+ * device signature). This is worked around by merging the SCR values
+ * into one sensible value and requesting follow-up SRST after
+ * hardreset.
+ *
+ * SCR merging is perfomed in nibbles which is the unit contents in
+ * SCRs are organized. If two values are equal, the value is used.
+ * When they differ, merge table which lists precedence of possible
+ * values is consulted and the first match or the last entry when
+ * nothing matches is used. When there's no merge table for the
+ * specific nibble, value from the first port is used.
+ */
+static const int piix_sidx_map[] = {
+ [SCR_STATUS] = 0,
+ [SCR_ERROR] = 2,
+ [SCR_CONTROL] = 1,
+};
+
+static void piix_sidpr_sel(struct ata_device *dev, unsigned int reg)
+{
+ struct ata_port *ap = dev->link->ap;
+ struct piix_host_priv *hpriv = ap->host->private_data;
+
+ iowrite32(((ap->port_no * 2 + dev->devno) << 8) | piix_sidx_map[reg],
+ hpriv->sidpr + PIIX_SIDPR_IDX);
+}
+
+static int piix_sidpr_read(struct ata_device *dev, unsigned int reg)
+{
+ struct piix_host_priv *hpriv = dev->link->ap->host->private_data;
+
+ piix_sidpr_sel(dev, reg);
+ return ioread32(hpriv->sidpr + PIIX_SIDPR_DATA);
+}
+
+static void piix_sidpr_write(struct ata_device *dev, unsigned int reg, u32 val)
+{
+ struct piix_host_priv *hpriv = dev->link->ap->host->private_data;
+
+ piix_sidpr_sel(dev, reg);
+ iowrite32(val, hpriv->sidpr + PIIX_SIDPR_DATA);
+}
+
+u32 piix_merge_scr(u32 val0, u32 val1, const int * const *merge_tbl)
+{
+ u32 val = 0;
+ int i, mi;
+
+ for (i = 0, mi = 0; i < 32 / 4; i++) {
+ u8 c0 = (val0 >> (i * 4)) & 0xf;
+ u8 c1 = (val1 >> (i * 4)) & 0xf;
+ u8 merged = c0;
+ const int *cur;
+
+ /* if no merge preference, assume the first value */
+ cur = merge_tbl[mi];
+ if (!cur)
+ goto done;
+ mi++;
+
+ /* if two values equal, use it */
+ if (c0 == c1)
+ goto done;
+
+ /* choose the first match or the last from the merge table */
+ while (*cur != -1) {
+ if (c0 == *cur || c1 == *cur)
+ break;
+ cur++;
+ }
+ if (*cur == -1)
+ cur--;
+ merged = *cur;
+ done:
+ val |= merged << (i * 4);
+ }
+
+ return val;
+}
+
+static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val)
+{
+ const int * const sstatus_merge_tbl[] = {
+ /* DET */ (const int []){ 1, 3, 0, 4, 3, -1 },
+ /* SPD */ (const int []){ 2, 1, 0, -1 },
+ /* IPM */ (const int []){ 6, 2, 1, 0, -1 },
+ NULL,
+ };
+ const int * const scontrol_merge_tbl[] = {
+ /* DET */ (const int []){ 1, 0, 4, 0, -1 },
+ /* SPD */ (const int []){ 0, 2, 1, 0, -1 },
+ /* IPM */ (const int []){ 0, 1, 2, 3, 0, -1 },
+ NULL,
+ };
+ u32 v0, v1;
+
+ if (reg >= ARRAY_SIZE(piix_sidx_map))
+ return -EINVAL;
+
+ if (!(ap->flags & ATA_FLAG_SLAVE_POSS)) {
+ *val = piix_sidpr_read(&ap->link.device[0], reg);
+ return 0;
+ }
+
+ v0 = piix_sidpr_read(&ap->link.device[0], reg);
+ v1 = piix_sidpr_read(&ap->link.device[1], reg);
+
+ switch (reg) {
+ case SCR_STATUS:
+ *val = piix_merge_scr(v0, v1, sstatus_merge_tbl);
+ break;
+ case SCR_ERROR:
+ *val = v0 | v1;
+ break;
+ case SCR_CONTROL:
+ *val = piix_merge_scr(v0, v1, scontrol_merge_tbl);
+ break;
+ }
+
+ return 0;
+}
+
+static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val)
+{
+ if (reg >= ARRAY_SIZE(piix_sidx_map))
+ return -EINVAL;
+
+ piix_sidpr_write(&ap->link.device[0], reg, val);
+
+ if (ap->flags & ATA_FLAG_SLAVE_POSS)
+ piix_sidpr_write(&ap->link.device[1], reg, val);
+
+ return 0;
+}
+
+static int piix_sidpr_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
+{
+ const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ int rc;
+
+ /* do hardreset */
+ rc = sata_link_hardreset(link, timing, deadline);
+ if (rc) {
+ ata_link_printk(link, KERN_ERR,
+ "COMRESET failed (errno=%d)\n", rc);
+ return rc;
+ }
+
+ /* TODO: phy layer with polling, timeouts, etc. */
+ if (ata_link_offline(link)) {
+ *class = ATA_DEV_NONE;
+ return 0;
+ }
+
+ return -EAGAIN;
+}
+
+static void piix_sidpr_error_handler(struct ata_port *ap)
+{
+ ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset,
+ piix_sidpr_hardreset, ata_std_postreset);
+}
+
#ifdef CONFIG_PM
static int piix_broken_suspend(void)
{
@@ -1033,6 +1221,13 @@ static int piix_broken_suspend(void)
DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M5"),
},
},
+ {
+ .ident = "TECRA M6",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M6"),
+ },
+ },
{
.ident = "TECRA M7",
.matches = {
@@ -1047,6 +1242,13 @@ static int piix_broken_suspend(void)
DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A8"),
},
},
+ {
+ .ident = "Satellite R20",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Satellite R20"),
+ },
+ },
{
.ident = "Satellite R25",
.matches = {
@@ -1253,10 +1455,10 @@ static int __devinit piix_check_450nx_errata(struct pci_dev *ata_dev)
return no_piix_dma;
}
-static void __devinit piix_init_pcs(struct pci_dev *pdev,
- struct ata_port_info *pinfo,
+static void __devinit piix_init_pcs(struct ata_host *host,
const struct piix_map_db *map_db)
{
+ struct pci_dev *pdev = to_pci_dev(host->dev);
u16 pcs, new_pcs;
pci_read_config_word(pdev, ICH5_PCS, &pcs);
@@ -1270,11 +1472,10 @@ static void __devinit piix_init_pcs(struct pci_dev *pdev,
}
}
-static void __devinit piix_init_sata_map(struct pci_dev *pdev,
- struct ata_port_info *pinfo,
- const struct piix_map_db *map_db)
+static const int *__devinit piix_init_sata_map(struct pci_dev *pdev,
+ struct ata_port_info *pinfo,
+ const struct piix_map_db *map_db)
{
- struct piix_host_priv *hpriv = pinfo[0].private_data;
const int *map;
int i, invalid_map = 0;
u8 map_value;
@@ -1298,7 +1499,6 @@ static void __devinit piix_init_sata_map(struct pci_dev *pdev,
case IDE:
WARN_ON((i & 1) || map[i + 1] != IDE);
pinfo[i / 2] = piix_port_info[ich_pata_100];
- pinfo[i / 2].private_data = hpriv;
i++;
printk(" IDE IDE");
break;
@@ -1316,7 +1516,33 @@ static void __devinit piix_init_sata_map(struct pci_dev *pdev,
dev_printk(KERN_ERR, &pdev->dev,
"invalid MAP value %u\n", map_value);
- hpriv->map = map;
+ return map;
+}
+
+static void __devinit piix_init_sidpr(struct ata_host *host)
+{
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ struct piix_host_priv *hpriv = host->private_data;
+ int i;
+
+ /* check for availability */
+ for (i = 0; i < 4; i++)
+ if (hpriv->map[i] == IDE)
+ return;
+
+ if (!(host->ports[0]->flags & PIIX_FLAG_SIDPR))
+ return;
+
+ if (pci_resource_start(pdev, PIIX_SIDPR_BAR) == 0 ||
+ pci_resource_len(pdev, PIIX_SIDPR_BAR) != PIIX_SIDPR_LEN)
+ return;
+
+ if (pcim_iomap_regions(pdev, 1 << PIIX_SIDPR_BAR, DRV_NAME))
+ return;
+
+ hpriv->sidpr = pcim_iomap_table(pdev)[PIIX_SIDPR_BAR];
+ host->ports[0]->ops = &piix_sidpr_sata_ops;
+ host->ports[1]->ops = &piix_sidpr_sata_ops;
}
static void piix_iocfg_bit18_quirk(struct pci_dev *pdev)
@@ -1375,8 +1601,10 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
struct device *dev = &pdev->dev;
struct ata_port_info port_info[2];
const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] };
- struct piix_host_priv *hpriv;
unsigned long port_flags;
+ struct ata_host *host;
+ struct piix_host_priv *hpriv;
+ int rc;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev,
@@ -1386,17 +1614,31 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!in_module_init)
return -ENODEV;
+ port_info[0] = piix_port_info[ent->driver_data];
+ port_info[1] = piix_port_info[ent->driver_data];
+
+ port_flags = port_info[0].flags;
+
+ /* enable device and prepare host */
+ rc = pcim_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ /* SATA map init can change port_info, do it before prepping host */
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
- port_info[0] = piix_port_info[ent->driver_data];
- port_info[1] = piix_port_info[ent->driver_data];
- port_info[0].private_data = hpriv;
- port_info[1].private_data = hpriv;
+ if (port_flags & ATA_FLAG_SATA)
+ hpriv->map = piix_init_sata_map(pdev, port_info,
+ piix_map_db_table[ent->driver_data]);
- port_flags = port_info[0].flags;
+ rc = ata_pci_prepare_sff_host(pdev, ppi, &host);
+ if (rc)
+ return rc;
+ host->private_data = hpriv;
+ /* initialize controller */
if (port_flags & PIIX_FLAG_AHCI) {
u8 tmp;
pci_read_config_byte(pdev, PIIX_SCC, &tmp);
@@ -1407,12 +1649,9 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
}
- /* Initialize SATA map */
if (port_flags & ATA_FLAG_SATA) {
- piix_init_sata_map(pdev, port_info,
- piix_map_db_table[ent->driver_data]);
- piix_init_pcs(pdev, port_info,
- piix_map_db_table[ent->driver_data]);
+ piix_init_pcs(host, piix_map_db_table[ent->driver_data]);
+ piix_init_sidpr(host);
}
/* apply IOCFG bit18 quirk */
@@ -1431,12 +1670,14 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* This writes into the master table but it does not
really matter for this errata as we will apply it to
all the PIIX devices on the board */
- port_info[0].mwdma_mask = 0;
- port_info[0].udma_mask = 0;
- port_info[1].mwdma_mask = 0;
- port_info[1].udma_mask = 0;
+ host->ports[0]->mwdma_mask = 0;
+ host->ports[0]->udma_mask = 0;
+ host->ports[1]->mwdma_mask = 0;
+ host->ports[1]->udma_mask = 0;
}
- return ata_pci_init_one(pdev, ppi);
+
+ pci_set_master(pdev);
+ return ata_pci_activate_sff_host(host, ata_interrupt, &piix_sht);
}
static int __init piix_init(void)
diff --git a/trunk/drivers/ata/libata-acpi.c b/trunk/drivers/ata/libata-acpi.c
index 7bf4befd96bc..9e8ec19260af 100644
--- a/trunk/drivers/ata/libata-acpi.c
+++ b/trunk/drivers/ata/libata-acpi.c
@@ -441,41 +441,78 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf)
return rc;
}
+/**
+ * ata_acpi_gtm_xfermode - determine xfermode from GTM parameter
+ * @dev: target device
+ * @gtm: GTM parameter to use
+ *
+ * Determine xfermask for @dev from @gtm.
+ *
+ * LOCKING:
+ * None.
+ *
+ * RETURNS:
+ * Determined xfermask.
+ */
+unsigned long ata_acpi_gtm_xfermask(struct ata_device *dev,
+ const struct ata_acpi_gtm *gtm)
+{
+ unsigned long xfer_mask = 0;
+ unsigned int type;
+ int unit;
+ u8 mode;
+
+ /* we always use the 0 slot for crap hardware */
+ unit = dev->devno;
+ if (!(gtm->flags & 0x10))
+ unit = 0;
+
+ /* PIO */
+ mode = ata_timing_cycle2mode(ATA_SHIFT_PIO, gtm->drive[unit].pio);
+ xfer_mask |= ata_xfer_mode2mask(mode);
+
+ /* See if we have MWDMA or UDMA data. We don't bother with
+ * MWDMA if UDMA is available as this means the BIOS set UDMA
+ * and our error changedown if it works is UDMA to PIO anyway.
+ */
+ if (!(gtm->flags & (1 << (2 * unit))))
+ type = ATA_SHIFT_MWDMA;
+ else
+ type = ATA_SHIFT_UDMA;
+
+ mode = ata_timing_cycle2mode(type, gtm->drive[unit].dma);
+ xfer_mask |= ata_xfer_mode2mask(mode);
+
+ return xfer_mask;
+}
+EXPORT_SYMBOL_GPL(ata_acpi_gtm_xfermask);
+
/**
* ata_acpi_cbl_80wire - Check for 80 wire cable
* @ap: Port to check
+ * @gtm: GTM data to use
*
- * Return 1 if the ACPI mode data for this port indicates the BIOS selected
- * an 80wire mode.
+ * Return 1 if the @gtm indicates the BIOS selected an 80wire mode.
*/
-
-int ata_acpi_cbl_80wire(struct ata_port *ap)
+int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm)
{
- const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap);
- int valid = 0;
+ struct ata_device *dev;
- if (!gtm)
- return 0;
+ ata_link_for_each_dev(dev, &ap->link) {
+ unsigned long xfer_mask, udma_mask;
+
+ if (!ata_dev_enabled(dev))
+ continue;
+
+ xfer_mask = ata_acpi_gtm_xfermask(dev, gtm);
+ ata_unpack_xfermask(xfer_mask, NULL, NULL, &udma_mask);
+
+ if (udma_mask & ~ATA_UDMA_MASK_40C)
+ return 1;
+ }
- /* Split timing, DMA enabled */
- if ((gtm->flags & 0x11) == 0x11 && gtm->drive[0].dma < 55)
- valid |= 1;
- if ((gtm->flags & 0x14) == 0x14 && gtm->drive[1].dma < 55)
- valid |= 2;
- /* Shared timing, DMA enabled */
- if ((gtm->flags & 0x11) == 0x01 && gtm->drive[0].dma < 55)
- valid |= 1;
- if ((gtm->flags & 0x14) == 0x04 && gtm->drive[0].dma < 55)
- valid |= 2;
-
- /* Drive check */
- if ((valid & 1) && ata_dev_enabled(&ap->link.device[0]))
- return 1;
- if ((valid & 2) && ata_dev_enabled(&ap->link.device[1]))
- return 1;
return 0;
}
-
EXPORT_SYMBOL_GPL(ata_acpi_cbl_80wire);
static void ata_acpi_gtf_to_tf(struct ata_device *dev,
@@ -775,6 +812,36 @@ void ata_acpi_on_resume(struct ata_port *ap)
}
}
+/**
+ * ata_acpi_set_state - set the port power state
+ * @ap: target ATA port
+ * @state: state, on/off
+ *
+ * This function executes the _PS0/_PS3 ACPI method to set the power state.
+ * ACPI spec requires _PS0 when IDE power on and _PS3 when power off
+ */
+void ata_acpi_set_state(struct ata_port *ap, pm_message_t state)
+{
+ struct ata_device *dev;
+
+ if (!ap->acpi_handle || (ap->flags & ATA_FLAG_ACPI_SATA))
+ return;
+
+ /* channel first and then drives for power on and vica versa
+ for power off */
+ if (state.event == PM_EVENT_ON)
+ acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D0);
+
+ ata_link_for_each_dev(dev, &ap->link) {
+ if (dev->acpi_handle && ata_dev_enabled(dev))
+ acpi_bus_set_power(dev->acpi_handle,
+ state.event == PM_EVENT_ON ?
+ ACPI_STATE_D0 : ACPI_STATE_D3);
+ }
+ if (state.event != PM_EVENT_ON)
+ acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D3);
+}
+
/**
* ata_acpi_on_devcfg - ATA ACPI hook called on device donfiguration
* @dev: target ATA device
diff --git a/trunk/drivers/ata/libata-core.c b/trunk/drivers/ata/libata-core.c
index 6380726f7538..ce803d18e96a 100644
--- a/trunk/drivers/ata/libata-core.c
+++ b/trunk/drivers/ata/libata-core.c
@@ -119,6 +119,10 @@ int libata_noacpi = 0;
module_param_named(noacpi, libata_noacpi, int, 0444);
MODULE_PARM_DESC(noacpi, "Disables the use of ACPI in probe/suspend/resume when set");
+int libata_allow_tpm = 0;
+module_param_named(allow_tpm, libata_allow_tpm, int, 0444);
+MODULE_PARM_DESC(allow_tpm, "Permit the use of TPM commands");
+
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Library module for ATA devices");
MODULE_LICENSE("GPL");
@@ -450,9 +454,9 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
* RETURNS:
* Packed xfer_mask.
*/
-static unsigned int ata_pack_xfermask(unsigned int pio_mask,
- unsigned int mwdma_mask,
- unsigned int udma_mask)
+unsigned long ata_pack_xfermask(unsigned long pio_mask,
+ unsigned long mwdma_mask,
+ unsigned long udma_mask)
{
return ((pio_mask << ATA_SHIFT_PIO) & ATA_MASK_PIO) |
((mwdma_mask << ATA_SHIFT_MWDMA) & ATA_MASK_MWDMA) |
@@ -469,10 +473,8 @@ static unsigned int ata_pack_xfermask(unsigned int pio_mask,
* Unpack @xfer_mask into @pio_mask, @mwdma_mask and @udma_mask.
* Any NULL distination masks will be ignored.
*/
-static void ata_unpack_xfermask(unsigned int xfer_mask,
- unsigned int *pio_mask,
- unsigned int *mwdma_mask,
- unsigned int *udma_mask)
+void ata_unpack_xfermask(unsigned long xfer_mask, unsigned long *pio_mask,
+ unsigned long *mwdma_mask, unsigned long *udma_mask)
{
if (pio_mask)
*pio_mask = (xfer_mask & ATA_MASK_PIO) >> ATA_SHIFT_PIO;
@@ -486,9 +488,9 @@ static const struct ata_xfer_ent {
int shift, bits;
u8 base;
} ata_xfer_tbl[] = {
- { ATA_SHIFT_PIO, ATA_BITS_PIO, XFER_PIO_0 },
- { ATA_SHIFT_MWDMA, ATA_BITS_MWDMA, XFER_MW_DMA_0 },
- { ATA_SHIFT_UDMA, ATA_BITS_UDMA, XFER_UDMA_0 },
+ { ATA_SHIFT_PIO, ATA_NR_PIO_MODES, XFER_PIO_0 },
+ { ATA_SHIFT_MWDMA, ATA_NR_MWDMA_MODES, XFER_MW_DMA_0 },
+ { ATA_SHIFT_UDMA, ATA_NR_UDMA_MODES, XFER_UDMA_0 },
{ -1, },
};
@@ -503,9 +505,9 @@ static const struct ata_xfer_ent {
* None.
*
* RETURNS:
- * Matching XFER_* value, 0 if no match found.
+ * Matching XFER_* value, 0xff if no match found.
*/
-static u8 ata_xfer_mask2mode(unsigned int xfer_mask)
+u8 ata_xfer_mask2mode(unsigned long xfer_mask)
{
int highbit = fls(xfer_mask) - 1;
const struct ata_xfer_ent *ent;
@@ -513,7 +515,7 @@ static u8 ata_xfer_mask2mode(unsigned int xfer_mask)
for (ent = ata_xfer_tbl; ent->shift >= 0; ent++)
if (highbit >= ent->shift && highbit < ent->shift + ent->bits)
return ent->base + highbit - ent->shift;
- return 0;
+ return 0xff;
}
/**
@@ -528,13 +530,14 @@ static u8 ata_xfer_mask2mode(unsigned int xfer_mask)
* RETURNS:
* Matching xfer_mask, 0 if no match found.
*/
-static unsigned int ata_xfer_mode2mask(u8 xfer_mode)
+unsigned long ata_xfer_mode2mask(u8 xfer_mode)
{
const struct ata_xfer_ent *ent;
for (ent = ata_xfer_tbl; ent->shift >= 0; ent++)
if (xfer_mode >= ent->base && xfer_mode < ent->base + ent->bits)
- return 1 << (ent->shift + xfer_mode - ent->base);
+ return ((2 << (ent->shift + xfer_mode - ent->base)) - 1)
+ & ~((1 << ent->shift) - 1);
return 0;
}
@@ -550,7 +553,7 @@ static unsigned int ata_xfer_mode2mask(u8 xfer_mode)
* RETURNS:
* Matching xfer_shift, -1 if no match found.
*/
-static int ata_xfer_mode2shift(unsigned int xfer_mode)
+int ata_xfer_mode2shift(unsigned long xfer_mode)
{
const struct ata_xfer_ent *ent;
@@ -574,7 +577,7 @@ static int ata_xfer_mode2shift(unsigned int xfer_mode)
* Constant C string representing highest speed listed in
* @mode_mask, or the constant C string "".
*/
-static const char *ata_mode_string(unsigned int xfer_mask)
+const char *ata_mode_string(unsigned long xfer_mask)
{
static const char * const xfer_mode_str[] = {
"PIO0",
@@ -947,8 +950,8 @@ unsigned int ata_dev_try_classify(struct ata_device *dev, int present,
if (r_err)
*r_err = err;
- /* see if device passed diags: if master then continue and warn later */
- if (err == 0 && dev->devno == 0)
+ /* see if device passed diags: continue and warn later */
+ if (err == 0)
/* diagnostic fail : do nothing _YET_ */
dev->horkage |= ATA_HORKAGE_DIAGNOSTIC;
else if (err == 1)
@@ -1285,48 +1288,6 @@ static int ata_hpa_resize(struct ata_device *dev)
return 0;
}
-/**
- * ata_id_to_dma_mode - Identify DMA mode from id block
- * @dev: device to identify
- * @unknown: mode to assume if we cannot tell
- *
- * Set up the timing values for the device based upon the identify
- * reported values for the DMA mode. This function is used by drivers
- * which rely upon firmware configured modes, but wish to report the
- * mode correctly when possible.
- *
- * In addition we emit similarly formatted messages to the default
- * ata_dev_set_mode handler, in order to provide consistency of
- * presentation.
- */
-
-void ata_id_to_dma_mode(struct ata_device *dev, u8 unknown)
-{
- unsigned int mask;
- u8 mode;
-
- /* Pack the DMA modes */
- mask = ((dev->id[63] >> 8) << ATA_SHIFT_MWDMA) & ATA_MASK_MWDMA;
- if (dev->id[53] & 0x04)
- mask |= ((dev->id[88] >> 8) << ATA_SHIFT_UDMA) & ATA_MASK_UDMA;
-
- /* Select the mode in use */
- mode = ata_xfer_mask2mode(mask);
-
- if (mode != 0) {
- ata_dev_printk(dev, KERN_INFO, "configured for %s\n",
- ata_mode_string(mask));
- } else {
- /* SWDMA perhaps ? */
- mode = unknown;
- ata_dev_printk(dev, KERN_INFO, "configured for DMA\n");
- }
-
- /* Configure the device reporting */
- dev->xfer_mode = mode;
- dev->xfer_shift = ata_xfer_mode2shift(mode);
-}
-
/**
* ata_noop_dev_select - Select device 0/1 on ATA bus
* @ap: ATA channel to manipulate
@@ -1464,9 +1425,9 @@ static inline void ata_dump_id(const u16 *id)
* RETURNS:
* Computed xfermask
*/
-static unsigned int ata_id_xfermask(const u16 *id)
+unsigned long ata_id_xfermask(const u16 *id)
{
- unsigned int pio_mask, mwdma_mask, udma_mask;
+ unsigned long pio_mask, mwdma_mask, udma_mask;
/* Usual case. Word 53 indicates word 64 is valid */
if (id[ATA_ID_FIELD_VALID] & (1 << 1)) {
@@ -1519,7 +1480,7 @@ static unsigned int ata_id_xfermask(const u16 *id)
}
/**
- * ata_port_queue_task - Queue port_task
+ * ata_pio_queue_task - Queue port_task
* @ap: The ata_port to queue port_task for
* @fn: workqueue function to be scheduled
* @data: data for @fn to use
@@ -1531,16 +1492,15 @@ static unsigned int ata_id_xfermask(const u16 *id)
* one task is active at any given time.
*
* libata core layer takes care of synchronization between
- * port_task and EH. ata_port_queue_task() may be ignored for EH
+ * port_task and EH. ata_pio_queue_task() may be ignored for EH
* synchronization.
*
* LOCKING:
* Inherited from caller.
*/
-void ata_port_queue_task(struct ata_port *ap, work_func_t fn, void *data,
- unsigned long delay)
+static void ata_pio_queue_task(struct ata_port *ap, void *data,
+ unsigned long delay)
{
- PREPARE_DELAYED_WORK(&ap->port_task, fn);
ap->port_task_data = data;
/* may fail if ata_port_flush_task() in progress */
@@ -2090,7 +2050,7 @@ int ata_dev_configure(struct ata_device *dev)
struct ata_eh_context *ehc = &dev->link->eh_context;
int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
const u16 *id = dev->id;
- unsigned int xfer_mask;
+ unsigned long xfer_mask;
char revbuf[7]; /* XYZ-99\0 */
char fwrevbuf[ATA_ID_FW_REV_LEN+1];
char modelbuf[ATA_ID_PROD_LEN+1];
@@ -2161,8 +2121,14 @@ int ata_dev_configure(struct ata_device *dev)
"supports DRM functions and may "
"not be fully accessable.\n");
snprintf(revbuf, 7, "CFA");
- } else
+ } else {
snprintf(revbuf, 7, "ATA-%d", ata_id_major_version(id));
+ /* Warn the user if the device has TPM extensions */
+ if (ata_id_has_tpm(id))
+ ata_dev_printk(dev, KERN_WARNING,
+ "supports DRM functions and may "
+ "not be fully accessable.\n");
+ }
dev->n_sectors = ata_id_n_sectors(id);
@@ -2295,19 +2261,8 @@ int ata_dev_configure(struct ata_device *dev)
dev->flags |= ATA_DFLAG_DIPM;
}
- if (dev->horkage & ATA_HORKAGE_DIAGNOSTIC) {
- /* Let the user know. We don't want to disallow opens for
- rescue purposes, or in case the vendor is just a blithering
- idiot */
- if (print_info) {
- ata_dev_printk(dev, KERN_WARNING,
-"Drive reports diagnostics failure. This may indicate a drive\n");
- ata_dev_printk(dev, KERN_WARNING,
-"fault or invalid emulation. Contact drive vendor for information.\n");
- }
- }
-
- /* limit bridge transfers to udma5, 200 sectors */
+ /* Limit PATA drive on SATA cable bridge transfers to udma5,
+ 200 sectors */
if (ata_dev_knobble(dev)) {
if (ata_msg_drv(ap) && print_info)
ata_dev_printk(dev, KERN_INFO,
@@ -2336,6 +2291,21 @@ int ata_dev_configure(struct ata_device *dev)
if (ap->ops->dev_config)
ap->ops->dev_config(dev);
+ if (dev->horkage & ATA_HORKAGE_DIAGNOSTIC) {
+ /* Let the user know. We don't want to disallow opens for
+ rescue purposes, or in case the vendor is just a blithering
+ idiot. Do this after the dev_config call as some controllers
+ with buggy firmware may want to avoid reporting false device
+ bugs */
+
+ if (print_info) {
+ ata_dev_printk(dev, KERN_WARNING,
+"Drive reports diagnostics failure. This may indicate a drive\n");
+ ata_dev_printk(dev, KERN_WARNING,
+"fault or invalid emulation. Contact drive vendor for information.\n");
+ }
+ }
+
if (ata_msg_probe(ap))
ata_dev_printk(dev, KERN_DEBUG, "%s: EXIT, drv_stat = 0x%x\n",
__FUNCTION__, ata_chk_status(ap));
@@ -2386,6 +2356,18 @@ int ata_cable_unknown(struct ata_port *ap)
return ATA_CBL_PATA_UNK;
}
+/**
+ * ata_cable_ignore - return ignored PATA cable.
+ * @ap: port
+ *
+ * Helper method for drivers which don't use cable type to limit
+ * transfer mode.
+ */
+int ata_cable_ignore(struct ata_port *ap)
+{
+ return ATA_CBL_PATA_IGN;
+}
+
/**
* ata_cable_sata - return SATA cable type
* @ap: port
@@ -2781,38 +2763,33 @@ int sata_set_spd(struct ata_link *link)
*/
static const struct ata_timing ata_timing[] = {
+/* { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 }, */
+ { XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 },
+ { XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 },
+ { XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 },
+ { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 },
+ { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 },
+ { XFER_PIO_5, 15, 65, 25, 100, 65, 25, 100, 0 },
+ { XFER_PIO_6, 10, 55, 20, 80, 55, 20, 80, 0 },
- { XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 },
- { XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 },
- { XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 },
- { XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 },
+ { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 },
+ { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 },
+ { XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 },
- { XFER_MW_DMA_4, 25, 0, 0, 0, 55, 20, 80, 0 },
+ { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 },
+ { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 },
+ { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 },
{ XFER_MW_DMA_3, 25, 0, 0, 0, 65, 25, 100, 0 },
- { XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 },
- { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 },
- { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 },
+ { XFER_MW_DMA_4, 25, 0, 0, 0, 55, 20, 80, 0 },
/* { XFER_UDMA_SLOW, 0, 0, 0, 0, 0, 0, 0, 150 }, */
-
- { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 },
- { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 },
- { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 },
-
- { XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 },
- { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 },
- { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 },
-
- { XFER_PIO_6, 10, 55, 20, 80, 55, 20, 80, 0 },
- { XFER_PIO_5, 15, 65, 25, 100, 65, 25, 100, 0 },
- { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 },
- { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 },
-
- { XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 },
- { XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 },
- { XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 },
-
-/* { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 }, */
+ { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 },
+ { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 },
+ { XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 },
+ { XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 },
+ { XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 },
+ { XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 },
+ { XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 },
{ 0xFF }
};
@@ -2845,14 +2822,16 @@ void ata_timing_merge(const struct ata_timing *a, const struct ata_timing *b,
if (what & ATA_TIMING_UDMA ) m->udma = max(a->udma, b->udma);
}
-static const struct ata_timing *ata_timing_find_mode(unsigned short speed)
+const struct ata_timing *ata_timing_find_mode(u8 xfer_mode)
{
- const struct ata_timing *t;
+ const struct ata_timing *t = ata_timing;
+
+ while (xfer_mode > t->mode)
+ t++;
- for (t = ata_timing; t->mode != speed; t++)
- if (t->mode == 0xFF)
- return NULL;
- return t;
+ if (xfer_mode == t->mode)
+ return t;
+ return NULL;
}
int ata_timing_compute(struct ata_device *adev, unsigned short speed,
@@ -2926,6 +2905,57 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed,
return 0;
}
+/**
+ * ata_timing_cycle2mode - find xfer mode for the specified cycle duration
+ * @xfer_shift: ATA_SHIFT_* value for transfer type to examine.
+ * @cycle: cycle duration in ns
+ *
+ * Return matching xfer mode for @cycle. The returned mode is of
+ * the transfer type specified by @xfer_shift. If @cycle is too
+ * slow for @xfer_shift, 0xff is returned. If @cycle is faster
+ * than the fastest known mode, the fasted mode is returned.
+ *
+ * LOCKING:
+ * None.
+ *
+ * RETURNS:
+ * Matching xfer_mode, 0xff if no match found.
+ */
+u8 ata_timing_cycle2mode(unsigned int xfer_shift, int cycle)
+{
+ u8 base_mode = 0xff, last_mode = 0xff;
+ const struct ata_xfer_ent *ent;
+ const struct ata_timing *t;
+
+ for (ent = ata_xfer_tbl; ent->shift >= 0; ent++)
+ if (ent->shift == xfer_shift)
+ base_mode = ent->base;
+
+ for (t = ata_timing_find_mode(base_mode);
+ t && ata_xfer_mode2shift(t->mode) == xfer_shift; t++) {
+ unsigned short this_cycle;
+
+ switch (xfer_shift) {
+ case ATA_SHIFT_PIO:
+ case ATA_SHIFT_MWDMA:
+ this_cycle = t->cycle;
+ break;
+ case ATA_SHIFT_UDMA:
+ this_cycle = t->udma;
+ break;
+ default:
+ return 0xff;
+ }
+
+ if (cycle > this_cycle)
+ break;
+
+ last_mode = t->mode;
+ }
+
+ return last_mode;
+}
+
/**
* ata_down_xfermask_limit - adjust dev xfer masks downward
* @dev: Device to adjust xfer masks
@@ -2944,8 +2974,8 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed,
int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel)
{
char buf[32];
- unsigned int orig_mask, xfer_mask;
- unsigned int pio_mask, mwdma_mask, udma_mask;
+ unsigned long orig_mask, xfer_mask;
+ unsigned long pio_mask, mwdma_mask, udma_mask;
int quiet, highbit;
quiet = !!(sel & ATA_DNXFER_QUIET);
@@ -3039,7 +3069,7 @@ static int ata_dev_set_mode(struct ata_device *dev)
/* Early MWDMA devices do DMA but don't allow DMA mode setting.
Don't fail an MWDMA0 set IFF the device indicates it is in MWDMA0 */
- if (dev->xfer_shift == ATA_SHIFT_MWDMA &&
+ if (dev->xfer_shift == ATA_SHIFT_MWDMA &&
dev->dma_mode == XFER_MW_DMA_0 &&
(dev->id[63] >> 8) & 1)
err_mask &= ~AC_ERR_DEV;
@@ -3089,7 +3119,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
/* step 1: calculate xfer_mask */
ata_link_for_each_dev(dev, link) {
- unsigned int pio_mask, dma_mask;
+ unsigned long pio_mask, dma_mask;
unsigned int mode_mask;
if (!ata_dev_enabled(dev))
@@ -3115,7 +3145,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
dev->dma_mode = ata_xfer_mask2mode(dma_mask);
found = 1;
- if (dev->dma_mode)
+ if (dev->dma_mode != 0xff)
used_dma = 1;
}
if (!found)
@@ -3126,7 +3156,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
if (!ata_dev_enabled(dev))
continue;
- if (!dev->pio_mode) {
+ if (dev->pio_mode == 0xff) {
ata_dev_printk(dev, KERN_WARNING, "no PIO support\n");
rc = -EINVAL;
goto out;
@@ -3140,7 +3170,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
/* step 3: set host DMA timings */
ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev) || !dev->dma_mode)
+ if (!ata_dev_enabled(dev) || dev->dma_mode == 0xff)
continue;
dev->xfer_mode = dev->dma_mode;
@@ -3172,31 +3202,6 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
return rc;
}
-/**
- * ata_set_mode - Program timings and issue SET FEATURES - XFER
- * @link: link on which timings will be programmed
- * @r_failed_dev: out paramter for failed device
- *
- * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If
- * ata_set_mode() fails, pointer to the failing device is
- * returned in @r_failed_dev.
- *
- * LOCKING:
- * PCI/etc. bus probe sem.
- *
- * RETURNS:
- * 0 on success, negative errno otherwise
- */
-int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
-{
- struct ata_port *ap = link->ap;
-
- /* has private set_mode? */
- if (ap->ops->set_mode)
- return ap->ops->set_mode(link, r_failed_dev);
- return ata_do_set_mode(link, r_failed_dev);
-}
-
/**
* ata_tf_to_host - issue ATA taskfile to host controller
* @ap: port to which command is being issued
@@ -4363,7 +4368,14 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev)
tf.feature = SETFEATURES_XFER;
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_POLLING;
tf.protocol = ATA_PROT_NODATA;
- tf.nsect = dev->xfer_mode;
+ /* If we are using IORDY we must send the mode setting command */
+ if (ata_pio_need_iordy(dev))
+ tf.nsect = dev->xfer_mode;
+ /* If the device has IORDY and the controller does not - turn it off */
+ else if (ata_id_has_iordy(dev->id))
+ tf.nsect = 0x01;
+ else /* In the ancient relic department - skip all of this */
+ return 0;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
@@ -4462,17 +4474,13 @@ static unsigned int ata_dev_init_params(struct ata_device *dev,
void ata_sg_clean(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- struct scatterlist *sg = qc->__sg;
+ struct scatterlist *sg = qc->sg;
int dir = qc->dma_dir;
void *pad_buf = NULL;
- WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP));
WARN_ON(sg == NULL);
- if (qc->flags & ATA_QCFLAG_SINGLE)
- WARN_ON(qc->n_elem > 1);
-
- VPRINTK("unmapping %u sg elements\n", qc->n_elem);
+ VPRINTK("unmapping %u sg elements\n", qc->mapped_n_elem);
/* if we padded the buffer out to 32-bit bound, and data
* xfer direction is from-device, we must copy from the
@@ -4481,31 +4489,20 @@ void ata_sg_clean(struct ata_queued_cmd *qc)
if (qc->pad_len && !(qc->tf.flags & ATA_TFLAG_WRITE))
pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ);
- if (qc->flags & ATA_QCFLAG_SG) {
- if (qc->n_elem)
- dma_unmap_sg(ap->dev, sg, qc->n_elem, dir);
- /* restore last sg */
- sg_last(sg, qc->orig_n_elem)->length += qc->pad_len;
- if (pad_buf) {
- struct scatterlist *psg = &qc->pad_sgent;
- void *addr = kmap_atomic(sg_page(psg), KM_IRQ0);
- memcpy(addr + psg->offset, pad_buf, qc->pad_len);
- kunmap_atomic(addr, KM_IRQ0);
- }
- } else {
- if (qc->n_elem)
- dma_unmap_single(ap->dev,
- sg_dma_address(&sg[0]), sg_dma_len(&sg[0]),
- dir);
- /* restore sg */
- sg->length += qc->pad_len;
- if (pad_buf)
- memcpy(qc->buf_virt + sg->length - qc->pad_len,
- pad_buf, qc->pad_len);
+ if (qc->mapped_n_elem)
+ dma_unmap_sg(ap->dev, sg, qc->mapped_n_elem, dir);
+ /* restore last sg */
+ if (qc->last_sg)
+ *qc->last_sg = qc->saved_last_sg;
+ if (pad_buf) {
+ struct scatterlist *psg = &qc->extra_sg[1];
+ void *addr = kmap_atomic(sg_page(psg), KM_IRQ0);
+ memcpy(addr + psg->offset, pad_buf, qc->pad_len);
+ kunmap_atomic(addr, KM_IRQ0);
}
qc->flags &= ~ATA_QCFLAG_DMAMAP;
- qc->__sg = NULL;
+ qc->sg = NULL;
}
/**
@@ -4523,13 +4520,10 @@ static void ata_fill_sg(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct scatterlist *sg;
- unsigned int idx;
+ unsigned int si, pi;
- WARN_ON(qc->__sg == NULL);
- WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
-
- idx = 0;
- ata_for_each_sg(sg, qc) {
+ pi = 0;
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
u32 addr, offset;
u32 sg_len, len;
@@ -4546,18 +4540,17 @@ static void ata_fill_sg(struct ata_queued_cmd *qc)
if ((offset + sg_len) > 0x10000)
len = 0x10000 - offset;
- ap->prd[idx].addr = cpu_to_le32(addr);
- ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff);
- VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
+ ap->prd[pi].addr = cpu_to_le32(addr);
+ ap->prd[pi].flags_len = cpu_to_le32(len & 0xffff);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, len);
- idx++;
+ pi++;
sg_len -= len;
addr += len;
}
}
- if (idx)
- ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+ ap->prd[pi - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
}
/**
@@ -4577,13 +4570,10 @@ static void ata_fill_sg_dumb(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct scatterlist *sg;
- unsigned int idx;
-
- WARN_ON(qc->__sg == NULL);
- WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
+ unsigned int si, pi;
- idx = 0;
- ata_for_each_sg(sg, qc) {
+ pi = 0;
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
u32 addr, offset;
u32 sg_len, len, blen;
@@ -4601,25 +4591,24 @@ static void ata_fill_sg_dumb(struct ata_queued_cmd *qc)
len = 0x10000 - offset;
blen = len & 0xffff;
- ap->prd[idx].addr = cpu_to_le32(addr);
+ ap->prd[pi].addr = cpu_to_le32(addr);
if (blen == 0) {
/* Some PATA chipsets like the CS5530 can't
cope with 0x0000 meaning 64K as the spec says */
- ap->prd[idx].flags_len = cpu_to_le32(0x8000);
+ ap->prd[pi].flags_len = cpu_to_le32(0x8000);
blen = 0x8000;
- ap->prd[++idx].addr = cpu_to_le32(addr + 0x8000);
+ ap->prd[++pi].addr = cpu_to_le32(addr + 0x8000);
}
- ap->prd[idx].flags_len = cpu_to_le32(blen);
- VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
+ ap->prd[pi].flags_len = cpu_to_le32(blen);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, len);
- idx++;
+ pi++;
sg_len -= len;
addr += len;
}
}
- if (idx)
- ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+ ap->prd[pi - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
}
/**
@@ -4669,8 +4658,8 @@ int ata_check_atapi_dma(struct ata_queued_cmd *qc)
*/
static int atapi_qc_may_overflow(struct ata_queued_cmd *qc)
{
- if (qc->tf.protocol != ATA_PROT_ATAPI &&
- qc->tf.protocol != ATA_PROT_ATAPI_DMA)
+ if (qc->tf.protocol != ATAPI_PROT_PIO &&
+ qc->tf.protocol != ATAPI_PROT_DMA)
return 0;
if (qc->tf.flags & ATA_TFLAG_WRITE)
@@ -4755,33 +4744,6 @@ void ata_dumb_qc_prep(struct ata_queued_cmd *qc)
void ata_noop_qc_prep(struct ata_queued_cmd *qc) { }
-/**
- * ata_sg_init_one - Associate command with memory buffer
- * @qc: Command to be associated
- * @buf: Memory buffer
- * @buflen: Length of memory buffer, in bytes.
- *
- * Initialize the data-related elements of queued_cmd @qc
- * to point to a single memory buffer, @buf of byte length @buflen.
- *
- * LOCKING:
- * spin_lock_irqsave(host lock)
- */
-
-void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen)
-{
- qc->flags |= ATA_QCFLAG_SINGLE;
-
- qc->__sg = &qc->sgent;
- qc->n_elem = 1;
- qc->orig_n_elem = 1;
- qc->buf_virt = buf;
- qc->nbytes = buflen;
- qc->cursg = qc->__sg;
-
- sg_init_one(&qc->sgent, buf, buflen);
-}
-
/**
* ata_sg_init - Associate command with scatter-gather table.
* @qc: Command to be associated
@@ -4795,84 +4757,103 @@ void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-
void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg,
unsigned int n_elem)
{
- qc->flags |= ATA_QCFLAG_SG;
- qc->__sg = sg;
+ qc->sg = sg;
qc->n_elem = n_elem;
- qc->orig_n_elem = n_elem;
- qc->cursg = qc->__sg;
+ qc->cursg = qc->sg;
}
-/**
- * ata_sg_setup_one - DMA-map the memory buffer associated with a command.
- * @qc: Command with memory buffer to be mapped.
- *
- * DMA-map the memory buffer associated with queued_cmd @qc.
- *
- * LOCKING:
- * spin_lock_irqsave(host lock)
- *
- * RETURNS:
- * Zero on success, negative on error.
- */
-
-static int ata_sg_setup_one(struct ata_queued_cmd *qc)
+static unsigned int ata_sg_setup_extra(struct ata_queued_cmd *qc,
+ unsigned int *n_elem_extra,
+ unsigned int *nbytes_extra)
{
struct ata_port *ap = qc->ap;
- int dir = qc->dma_dir;
- struct scatterlist *sg = qc->__sg;
- dma_addr_t dma_address;
- int trim_sg = 0;
+ unsigned int n_elem = qc->n_elem;
+ struct scatterlist *lsg, *copy_lsg = NULL, *tsg = NULL, *esg = NULL;
+
+ *n_elem_extra = 0;
+ *nbytes_extra = 0;
+
+ /* needs padding? */
+ qc->pad_len = qc->nbytes & 3;
+
+ if (likely(!qc->pad_len))
+ return n_elem;
+
+ /* locate last sg and save it */
+ lsg = sg_last(qc->sg, n_elem);
+ qc->last_sg = lsg;
+ qc->saved_last_sg = *lsg;
+
+ sg_init_table(qc->extra_sg, ARRAY_SIZE(qc->extra_sg));
- /* we must lengthen transfers to end on a 32-bit boundary */
- qc->pad_len = sg->length & 3;
if (qc->pad_len) {
+ struct scatterlist *psg = &qc->extra_sg[1];
void *pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ);
- struct scatterlist *psg = &qc->pad_sgent;
+ unsigned int offset;
WARN_ON(qc->dev->class != ATA_DEV_ATAPI);
memset(pad_buf, 0, ATA_DMA_PAD_SZ);
- if (qc->tf.flags & ATA_TFLAG_WRITE)
- memcpy(pad_buf, qc->buf_virt + sg->length - qc->pad_len,
- qc->pad_len);
+ /* psg->page/offset are used to copy to-be-written
+ * data in this function or read data in ata_sg_clean.
+ */
+ offset = lsg->offset + lsg->length - qc->pad_len;
+ sg_set_page(psg, nth_page(sg_page(lsg), offset >> PAGE_SHIFT),
+ qc->pad_len, offset_in_page(offset));
+
+ if (qc->tf.flags & ATA_TFLAG_WRITE) {
+ void *addr = kmap_atomic(sg_page(psg), KM_IRQ0);
+ memcpy(pad_buf, addr + psg->offset, qc->pad_len);
+ kunmap_atomic(addr, KM_IRQ0);
+ }
sg_dma_address(psg) = ap->pad_dma + (qc->tag * ATA_DMA_PAD_SZ);
sg_dma_len(psg) = ATA_DMA_PAD_SZ;
- /* trim sg */
- sg->length -= qc->pad_len;
- if (sg->length == 0)
- trim_sg = 1;
- DPRINTK("padding done, sg->length=%u pad_len=%u\n",
- sg->length, qc->pad_len);
- }
+ /* Trim the last sg entry and chain the original and
+ * padding sg lists.
+ *
+ * Because chaining consumes one sg entry, one extra
+ * sg entry is allocated and the last sg entry is
+ * copied to it if the length isn't zero after padded
+ * amount is removed.
+ *
+ * If the last sg entry is completely replaced by
+ * padding sg entry, the first sg entry is skipped
+ * while chaining.
+ */
+ lsg->length -= qc->pad_len;
+ if (lsg->length) {
+ copy_lsg = &qc->extra_sg[0];
+ tsg = &qc->extra_sg[0];
+ } else {
+ n_elem--;
+ tsg = &qc->extra_sg[1];
+ }
- if (trim_sg) {
- qc->n_elem--;
- goto skip_map;
- }
+ esg = &qc->extra_sg[1];
- dma_address = dma_map_single(ap->dev, qc->buf_virt,
- sg->length, dir);
- if (dma_mapping_error(dma_address)) {
- /* restore sg */
- sg->length += qc->pad_len;
- return -1;
+ (*n_elem_extra)++;
+ (*nbytes_extra) += 4 - qc->pad_len;
}
- sg_dma_address(sg) = dma_address;
- sg_dma_len(sg) = sg->length;
+ if (copy_lsg)
+ sg_set_page(copy_lsg, sg_page(lsg), lsg->length, lsg->offset);
-skip_map:
- DPRINTK("mapped buffer of %d bytes for %s\n", sg_dma_len(sg),
- qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+ sg_chain(lsg, 1, tsg);
+ sg_mark_end(esg);
- return 0;
+ /* sglist can't start with chaining sg entry, fast forward */
+ if (qc->sg == lsg) {
+ qc->sg = tsg;
+ qc->cursg = tsg;
+ }
+
+ return n_elem;
}
/**
@@ -4888,75 +4869,30 @@ static int ata_sg_setup_one(struct ata_queued_cmd *qc)
* Zero on success, negative on error.
*
*/
-
static int ata_sg_setup(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- struct scatterlist *sg = qc->__sg;
- struct scatterlist *lsg = sg_last(qc->__sg, qc->n_elem);
- int n_elem, pre_n_elem, dir, trim_sg = 0;
+ unsigned int n_elem, n_elem_extra, nbytes_extra;
VPRINTK("ENTER, ata%u\n", ap->print_id);
- WARN_ON(!(qc->flags & ATA_QCFLAG_SG));
- /* we must lengthen transfers to end on a 32-bit boundary */
- qc->pad_len = lsg->length & 3;
- if (qc->pad_len) {
- void *pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ);
- struct scatterlist *psg = &qc->pad_sgent;
- unsigned int offset;
-
- WARN_ON(qc->dev->class != ATA_DEV_ATAPI);
+ n_elem = ata_sg_setup_extra(qc, &n_elem_extra, &nbytes_extra);
- memset(pad_buf, 0, ATA_DMA_PAD_SZ);
-
- /*
- * psg->page/offset are used to copy to-be-written
- * data in this function or read data in ata_sg_clean.
- */
- offset = lsg->offset + lsg->length - qc->pad_len;
- sg_init_table(psg, 1);
- sg_set_page(psg, nth_page(sg_page(lsg), offset >> PAGE_SHIFT),
- qc->pad_len, offset_in_page(offset));
-
- if (qc->tf.flags & ATA_TFLAG_WRITE) {
- void *addr = kmap_atomic(sg_page(psg), KM_IRQ0);
- memcpy(pad_buf, addr + psg->offset, qc->pad_len);
- kunmap_atomic(addr, KM_IRQ0);
+ if (n_elem) {
+ n_elem = dma_map_sg(ap->dev, qc->sg, n_elem, qc->dma_dir);
+ if (n_elem < 1) {
+ /* restore last sg */
+ if (qc->last_sg)
+ *qc->last_sg = qc->saved_last_sg;
+ return -1;
}
-
- sg_dma_address(psg) = ap->pad_dma + (qc->tag * ATA_DMA_PAD_SZ);
- sg_dma_len(psg) = ATA_DMA_PAD_SZ;
- /* trim last sg */
- lsg->length -= qc->pad_len;
- if (lsg->length == 0)
- trim_sg = 1;
-
- DPRINTK("padding done, sg[%d].length=%u pad_len=%u\n",
- qc->n_elem - 1, lsg->length, qc->pad_len);
- }
-
- pre_n_elem = qc->n_elem;
- if (trim_sg && pre_n_elem)
- pre_n_elem--;
-
- if (!pre_n_elem) {
- n_elem = 0;
- goto skip_map;
- }
-
- dir = qc->dma_dir;
- n_elem = dma_map_sg(ap->dev, sg, pre_n_elem, dir);
- if (n_elem < 1) {
- /* restore last sg */
- lsg->length += qc->pad_len;
- return -1;
+ DPRINTK("%d sg elements mapped\n", n_elem);
}
- DPRINTK("%d sg elements mapped\n", n_elem);
-
-skip_map:
- qc->n_elem = n_elem;
+ qc->n_elem = qc->mapped_n_elem = n_elem;
+ qc->n_elem += n_elem_extra;
+ qc->nbytes += nbytes_extra;
+ qc->flags |= ATA_QCFLAG_DMAMAP;
return 0;
}
@@ -4985,7 +4921,7 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words)
/**
* ata_data_xfer - Transfer data by PIO
- * @adev: device to target
+ * @dev: device to target
* @buf: data buffer
* @buflen: buffer length
* @write_data: read/write
@@ -4994,37 +4930,44 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words)
*
* LOCKING:
* Inherited from caller.
+ *
+ * RETURNS:
+ * Bytes consumed.
*/
-void ata_data_xfer(struct ata_device *adev, unsigned char *buf,
- unsigned int buflen, int write_data)
+unsigned int ata_data_xfer(struct ata_device *dev, unsigned char *buf,
+ unsigned int buflen, int rw)
{
- struct ata_port *ap = adev->link->ap;
+ struct ata_port *ap = dev->link->ap;
+ void __iomem *data_addr = ap->ioaddr.data_addr;
unsigned int words = buflen >> 1;
/* Transfer multiple of 2 bytes */
- if (write_data)
- iowrite16_rep(ap->ioaddr.data_addr, buf, words);
+ if (rw == READ)
+ ioread16_rep(data_addr, buf, words);
else
- ioread16_rep(ap->ioaddr.data_addr, buf, words);
+ iowrite16_rep(data_addr, buf, words);
/* Transfer trailing 1 byte, if any. */
if (unlikely(buflen & 0x01)) {
- u16 align_buf[1] = { 0 };
+ __le16 align_buf[1] = { 0 };
unsigned char *trailing_buf = buf + buflen - 1;
- if (write_data) {
- memcpy(align_buf, trailing_buf, 1);
- iowrite16(le16_to_cpu(align_buf[0]), ap->ioaddr.data_addr);
- } else {
- align_buf[0] = cpu_to_le16(ioread16(ap->ioaddr.data_addr));
+ if (rw == READ) {
+ align_buf[0] = cpu_to_le16(ioread16(data_addr));
memcpy(trailing_buf, align_buf, 1);
+ } else {
+ memcpy(align_buf, trailing_buf, 1);
+ iowrite16(le16_to_cpu(align_buf[0]), data_addr);
}
+ words++;
}
+
+ return words << 1;
}
/**
* ata_data_xfer_noirq - Transfer data by PIO
- * @adev: device to target
+ * @dev: device to target
* @buf: data buffer
* @buflen: buffer length
* @write_data: read/write
@@ -5034,14 +4977,21 @@ void ata_data_xfer(struct ata_device *adev, unsigned char *buf,
*
* LOCKING:
* Inherited from caller.
+ *
+ * RETURNS:
+ * Bytes consumed.
*/
-void ata_data_xfer_noirq(struct ata_device *adev, unsigned char *buf,
- unsigned int buflen, int write_data)
+unsigned int ata_data_xfer_noirq(struct ata_device *dev, unsigned char *buf,
+ unsigned int buflen, int rw)
{
unsigned long flags;
+ unsigned int consumed;
+
local_irq_save(flags);
- ata_data_xfer(adev, buf, buflen, write_data);
+ consumed = ata_data_xfer(dev, buf, buflen, rw);
local_irq_restore(flags);
+
+ return consumed;
}
@@ -5152,13 +5102,13 @@ static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc)
ata_altstatus(ap); /* flush */
switch (qc->tf.protocol) {
- case ATA_PROT_ATAPI:
+ case ATAPI_PROT_PIO:
ap->hsm_task_state = HSM_ST;
break;
- case ATA_PROT_ATAPI_NODATA:
+ case ATAPI_PROT_NODATA:
ap->hsm_task_state = HSM_ST_LAST;
break;
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
ap->hsm_task_state = HSM_ST_LAST;
/* initiate bmdma */
ap->ops->bmdma_start(qc);
@@ -5300,12 +5250,15 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc)
bytes = (bc_hi << 8) | bc_lo;
/* shall be cleared to zero, indicating xfer of data */
- if (ireason & (1 << 0))
+ if (unlikely(ireason & (1 << 0)))
goto err_out;
/* make sure transfer direction matches expected */
i_write = ((ireason & (1 << 1)) == 0) ? 1 : 0;
- if (do_write != i_write)
+ if (unlikely(do_write != i_write))
+ goto err_out;
+
+ if (unlikely(!bytes))
goto err_out;
VPRINTK("ata%u: xfering %d bytes\n", ap->print_id, bytes);
@@ -5341,7 +5294,7 @@ static inline int ata_hsm_ok_in_wq(struct ata_port *ap, struct ata_queued_cmd *q
(qc->tf.flags & ATA_TFLAG_WRITE))
return 1;
- if (is_atapi_taskfile(&qc->tf) &&
+ if (ata_is_atapi(qc->tf.protocol) &&
!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
return 1;
}
@@ -5506,7 +5459,7 @@ int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
case HSM_ST:
/* complete command or read/write the data register */
- if (qc->tf.protocol == ATA_PROT_ATAPI) {
+ if (qc->tf.protocol == ATAPI_PROT_PIO) {
/* ATAPI PIO protocol */
if ((status & ATA_DRQ) == 0) {
/* No more data to transfer or device error.
@@ -5664,7 +5617,7 @@ static void ata_pio_task(struct work_struct *work)
msleep(2);
status = ata_busy_wait(ap, ATA_BUSY, 10);
if (status & ATA_BUSY) {
- ata_port_queue_task(ap, ata_pio_task, qc, ATA_SHORT_PAUSE);
+ ata_pio_queue_task(ap, qc, ATA_SHORT_PAUSE);
return;
}
}
@@ -5805,6 +5758,22 @@ static void fill_result_tf(struct ata_queued_cmd *qc)
ap->ops->tf_read(ap, &qc->result_tf);
}
+static void ata_verify_xfer(struct ata_queued_cmd *qc)
+{
+ struct ata_device *dev = qc->dev;
+
+ if (ata_tag_internal(qc->tag))
+ return;
+
+ if (ata_is_nodata(qc->tf.protocol))
+ return;
+
+ if ((dev->mwdma_mask || dev->udma_mask) && ata_is_pio(qc->tf.protocol))
+ return;
+
+ dev->flags &= ~ATA_DFLAG_DUBIOUS_XFER;
+}
+
/**
* ata_qc_complete - Complete an active ATA command
* @qc: Command to complete
@@ -5876,6 +5845,9 @@ void ata_qc_complete(struct ata_queued_cmd *qc)
break;
}
+ if (unlikely(dev->flags & ATA_DFLAG_DUBIOUS_XFER))
+ ata_verify_xfer(qc);
+
__ata_qc_complete(qc);
} else {
if (qc->flags & ATA_QCFLAG_EH_SCHEDULED)
@@ -5938,30 +5910,6 @@ int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active,
return nr_done;
}
-static inline int ata_should_dma_map(struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
-
- switch (qc->tf.protocol) {
- case ATA_PROT_NCQ:
- case ATA_PROT_DMA:
- case ATA_PROT_ATAPI_DMA:
- return 1;
-
- case ATA_PROT_ATAPI:
- case ATA_PROT_PIO:
- if (ap->flags & ATA_FLAG_PIO_DMA)
- return 1;
-
- /* fall through */
-
- default:
- return 0;
- }
-
- /* never reached */
-}
-
/**
* ata_qc_issue - issue taskfile to device
* @qc: command to issue to device
@@ -5978,6 +5926,7 @@ void ata_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ata_link *link = qc->dev->link;
+ u8 prot = qc->tf.protocol;
/* Make sure only one non-NCQ command is outstanding. The
* check is skipped for old EH because it reuses active qc to
@@ -5985,7 +5934,7 @@ void ata_qc_issue(struct ata_queued_cmd *qc)
*/
WARN_ON(ap->ops->error_handler && ata_tag_valid(link->active_tag));
- if (qc->tf.protocol == ATA_PROT_NCQ) {
+ if (ata_is_ncq(prot)) {
WARN_ON(link->sactive & (1 << qc->tag));
if (!link->sactive)
@@ -6001,17 +5950,18 @@ void ata_qc_issue(struct ata_queued_cmd *qc)
qc->flags |= ATA_QCFLAG_ACTIVE;
ap->qc_active |= 1 << qc->tag;
- if (ata_should_dma_map(qc)) {
- if (qc->flags & ATA_QCFLAG_SG) {
- if (ata_sg_setup(qc))
- goto sg_err;
- } else if (qc->flags & ATA_QCFLAG_SINGLE) {
- if (ata_sg_setup_one(qc))
- goto sg_err;
- }
- } else {
- qc->flags &= ~ATA_QCFLAG_DMAMAP;
- }
+ /* We guarantee to LLDs that they will have at least one
+ * non-zero sg if the command is a data command.
+ */
+ BUG_ON(ata_is_data(prot) && (!qc->sg || !qc->n_elem || !qc->nbytes));
+
+ /* ata_sg_setup() may update nbytes */
+ qc->raw_nbytes = qc->nbytes;
+
+ if (ata_is_dma(prot) || (ata_is_pio(prot) &&
+ (ap->flags & ATA_FLAG_PIO_DMA)))
+ if (ata_sg_setup(qc))
+ goto sg_err;
/* if device is sleeping, schedule softreset and abort the link */
if (unlikely(qc->dev->flags & ATA_DFLAG_SLEEPING)) {
@@ -6029,7 +5979,6 @@ void ata_qc_issue(struct ata_queued_cmd *qc)
return;
sg_err:
- qc->flags &= ~ATA_QCFLAG_DMAMAP;
qc->err_mask |= AC_ERR_SYSTEM;
err:
ata_qc_complete(qc);
@@ -6064,11 +6013,11 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
switch (qc->tf.protocol) {
case ATA_PROT_PIO:
case ATA_PROT_NODATA:
- case ATA_PROT_ATAPI:
- case ATA_PROT_ATAPI_NODATA:
+ case ATAPI_PROT_PIO:
+ case ATAPI_PROT_NODATA:
qc->tf.flags |= ATA_TFLAG_POLLING;
break;
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
if (qc->dev->flags & ATA_DFLAG_CDB_INTR)
/* see ata_dma_blacklisted() */
BUG();
@@ -6091,7 +6040,7 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
ap->hsm_task_state = HSM_ST_LAST;
if (qc->tf.flags & ATA_TFLAG_POLLING)
- ata_port_queue_task(ap, ata_pio_task, qc, 0);
+ ata_pio_queue_task(ap, qc, 0);
break;
@@ -6113,7 +6062,7 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
if (qc->tf.flags & ATA_TFLAG_WRITE) {
/* PIO data out protocol */
ap->hsm_task_state = HSM_ST_FIRST;
- ata_port_queue_task(ap, ata_pio_task, qc, 0);
+ ata_pio_queue_task(ap, qc, 0);
/* always send first data block using
* the ata_pio_task() codepath.
@@ -6123,7 +6072,7 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
ap->hsm_task_state = HSM_ST;
if (qc->tf.flags & ATA_TFLAG_POLLING)
- ata_port_queue_task(ap, ata_pio_task, qc, 0);
+ ata_pio_queue_task(ap, qc, 0);
/* if polling, ata_pio_task() handles the rest.
* otherwise, interrupt handler takes over from here.
@@ -6132,8 +6081,8 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
break;
- case ATA_PROT_ATAPI:
- case ATA_PROT_ATAPI_NODATA:
+ case ATAPI_PROT_PIO:
+ case ATAPI_PROT_NODATA:
if (qc->tf.flags & ATA_TFLAG_POLLING)
ata_qc_set_polling(qc);
@@ -6144,10 +6093,10 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
/* send cdb by polling if no cdb interrupt */
if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) ||
(qc->tf.flags & ATA_TFLAG_POLLING))
- ata_port_queue_task(ap, ata_pio_task, qc, 0);
+ ata_pio_queue_task(ap, qc, 0);
break;
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
WARN_ON(qc->tf.flags & ATA_TFLAG_POLLING);
ap->ops->tf_load(ap, &qc->tf); /* load tf registers */
@@ -6156,7 +6105,7 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
/* send cdb by polling if no cdb interrupt */
if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
- ata_port_queue_task(ap, ata_pio_task, qc, 0);
+ ata_pio_queue_task(ap, qc, 0);
break;
default:
@@ -6200,15 +6149,15 @@ inline unsigned int ata_host_intr(struct ata_port *ap,
*/
/* Check the ATA_DFLAG_CDB_INTR flag is enough here.
- * The flag was turned on only for atapi devices.
- * No need to check is_atapi_taskfile(&qc->tf) again.
+ * The flag was turned on only for atapi devices. No
+ * need to check ata_is_atapi(qc->tf.protocol) again.
*/
if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
goto idle_irq;
break;
case HSM_ST_LAST:
if (qc->tf.protocol == ATA_PROT_DMA ||
- qc->tf.protocol == ATA_PROT_ATAPI_DMA) {
+ qc->tf.protocol == ATAPI_PROT_DMA) {
/* check status of DMA engine */
host_stat = ap->ops->bmdma_status(ap);
VPRINTK("ata%u: host_stat 0x%X\n",
@@ -6250,7 +6199,7 @@ inline unsigned int ata_host_intr(struct ata_port *ap,
ata_hsm_move(ap, qc, status, 0);
if (unlikely(qc->err_mask) && (qc->tf.protocol == ATA_PROT_DMA ||
- qc->tf.protocol == ATA_PROT_ATAPI_DMA))
+ qc->tf.protocol == ATAPI_PROT_DMA))
ata_ehi_push_desc(ehi, "BMDMA stat 0x%x", host_stat);
return 1; /* irq handled */
@@ -6772,7 +6721,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN;
#endif
- INIT_DELAYED_WORK(&ap->port_task, NULL);
+ INIT_DELAYED_WORK(&ap->port_task, ata_pio_task);
INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);
INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
INIT_LIST_HEAD(&ap->eh_done_q);
@@ -7589,7 +7538,6 @@ EXPORT_SYMBOL_GPL(ata_host_register);
EXPORT_SYMBOL_GPL(ata_host_activate);
EXPORT_SYMBOL_GPL(ata_host_detach);
EXPORT_SYMBOL_GPL(ata_sg_init);
-EXPORT_SYMBOL_GPL(ata_sg_init_one);
EXPORT_SYMBOL_GPL(ata_hsm_move);
EXPORT_SYMBOL_GPL(ata_qc_complete);
EXPORT_SYMBOL_GPL(ata_qc_complete_multiple);
@@ -7601,6 +7549,13 @@ EXPORT_SYMBOL_GPL(ata_std_dev_select);
EXPORT_SYMBOL_GPL(sata_print_link_status);
EXPORT_SYMBOL_GPL(ata_tf_to_fis);
EXPORT_SYMBOL_GPL(ata_tf_from_fis);
+EXPORT_SYMBOL_GPL(ata_pack_xfermask);
+EXPORT_SYMBOL_GPL(ata_unpack_xfermask);
+EXPORT_SYMBOL_GPL(ata_xfer_mask2mode);
+EXPORT_SYMBOL_GPL(ata_xfer_mode2mask);
+EXPORT_SYMBOL_GPL(ata_xfer_mode2shift);
+EXPORT_SYMBOL_GPL(ata_mode_string);
+EXPORT_SYMBOL_GPL(ata_id_xfermask);
EXPORT_SYMBOL_GPL(ata_check_status);
EXPORT_SYMBOL_GPL(ata_altstatus);
EXPORT_SYMBOL_GPL(ata_exec_command);
@@ -7643,7 +7598,6 @@ EXPORT_SYMBOL_GPL(ata_wait_register);
EXPORT_SYMBOL_GPL(ata_busy_sleep);
EXPORT_SYMBOL_GPL(ata_wait_after_reset);
EXPORT_SYMBOL_GPL(ata_wait_ready);
-EXPORT_SYMBOL_GPL(ata_port_queue_task);
EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
@@ -7662,18 +7616,20 @@ EXPORT_SYMBOL_GPL(ata_host_resume);
#endif /* CONFIG_PM */
EXPORT_SYMBOL_GPL(ata_id_string);
EXPORT_SYMBOL_GPL(ata_id_c_string);
-EXPORT_SYMBOL_GPL(ata_id_to_dma_mode);
EXPORT_SYMBOL_GPL(ata_scsi_simulate);
EXPORT_SYMBOL_GPL(ata_pio_need_iordy);
+EXPORT_SYMBOL_GPL(ata_timing_find_mode);
EXPORT_SYMBOL_GPL(ata_timing_compute);
EXPORT_SYMBOL_GPL(ata_timing_merge);
+EXPORT_SYMBOL_GPL(ata_timing_cycle2mode);
#ifdef CONFIG_PCI
EXPORT_SYMBOL_GPL(pci_test_config_bits);
EXPORT_SYMBOL_GPL(ata_pci_init_sff_host);
EXPORT_SYMBOL_GPL(ata_pci_init_bmdma);
EXPORT_SYMBOL_GPL(ata_pci_prepare_sff_host);
+EXPORT_SYMBOL_GPL(ata_pci_activate_sff_host);
EXPORT_SYMBOL_GPL(ata_pci_init_one);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
#ifdef CONFIG_PM
@@ -7715,4 +7671,5 @@ EXPORT_SYMBOL_GPL(ata_dev_try_classify);
EXPORT_SYMBOL_GPL(ata_cable_40wire);
EXPORT_SYMBOL_GPL(ata_cable_80wire);
EXPORT_SYMBOL_GPL(ata_cable_unknown);
+EXPORT_SYMBOL_GPL(ata_cable_ignore);
EXPORT_SYMBOL_GPL(ata_cable_sata);
diff --git a/trunk/drivers/ata/libata-eh.c b/trunk/drivers/ata/libata-eh.c
index 21a81cd148e4..4e31071acc02 100644
--- a/trunk/drivers/ata/libata-eh.c
+++ b/trunk/drivers/ata/libata-eh.c
@@ -46,9 +46,26 @@
#include "libata.h"
enum {
+ /* speed down verdicts */
ATA_EH_SPDN_NCQ_OFF = (1 << 0),
ATA_EH_SPDN_SPEED_DOWN = (1 << 1),
ATA_EH_SPDN_FALLBACK_TO_PIO = (1 << 2),
+ ATA_EH_SPDN_KEEP_ERRORS = (1 << 3),
+
+ /* error flags */
+ ATA_EFLAG_IS_IO = (1 << 0),
+ ATA_EFLAG_DUBIOUS_XFER = (1 << 1),
+
+ /* error categories */
+ ATA_ECAT_NONE = 0,
+ ATA_ECAT_ATA_BUS = 1,
+ ATA_ECAT_TOUT_HSM = 2,
+ ATA_ECAT_UNK_DEV = 3,
+ ATA_ECAT_DUBIOUS_NONE = 4,
+ ATA_ECAT_DUBIOUS_ATA_BUS = 5,
+ ATA_ECAT_DUBIOUS_TOUT_HSM = 6,
+ ATA_ECAT_DUBIOUS_UNK_DEV = 7,
+ ATA_ECAT_NR = 8,
};
/* Waiting in ->prereset can never be reliable. It's sometimes nice
@@ -213,12 +230,13 @@ void ata_port_pbar_desc(struct ata_port *ap, int bar, ssize_t offset,
if (offset < 0)
ata_port_desc(ap, "%s %s%llu@0x%llx", name, type, len, start);
else
- ata_port_desc(ap, "%s 0x%llx", name, start + offset);
+ ata_port_desc(ap, "%s 0x%llx", name,
+ start + (unsigned long long)offset);
}
#endif /* CONFIG_PCI */
-static void ata_ering_record(struct ata_ering *ering, int is_io,
+static void ata_ering_record(struct ata_ering *ering, unsigned int eflags,
unsigned int err_mask)
{
struct ata_ering_entry *ent;
@@ -229,11 +247,20 @@ static void ata_ering_record(struct ata_ering *ering, int is_io,
ering->cursor %= ATA_ERING_SIZE;
ent = &ering->ring[ering->cursor];
- ent->is_io = is_io;
+ ent->eflags = eflags;
ent->err_mask = err_mask;
ent->timestamp = get_jiffies_64();
}
+static struct ata_ering_entry *ata_ering_top(struct ata_ering *ering)
+{
+ struct ata_ering_entry *ent = &ering->ring[ering->cursor];
+
+ if (ent->err_mask)
+ return ent;
+ return NULL;
+}
+
static void ata_ering_clear(struct ata_ering *ering)
{
memset(ering, 0, sizeof(*ering));
@@ -445,9 +472,20 @@ void ata_scsi_error(struct Scsi_Host *host)
spin_lock_irqsave(ap->lock, flags);
__ata_port_for_each_link(link, ap) {
+ struct ata_eh_context *ehc = &link->eh_context;
+ struct ata_device *dev;
+
memset(&link->eh_context, 0, sizeof(link->eh_context));
link->eh_context.i = link->eh_info;
memset(&link->eh_info, 0, sizeof(link->eh_info));
+
+ ata_link_for_each_dev(dev, link) {
+ int devno = dev->devno;
+
+ ehc->saved_xfer_mode[devno] = dev->xfer_mode;
+ if (ata_ncq_enabled(dev))
+ ehc->saved_ncq_enabled |= 1 << devno;
+ }
}
ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
@@ -1260,10 +1298,10 @@ static unsigned int atapi_eh_request_sense(struct ata_queued_cmd *qc)
/* is it pointless to prefer PIO for "safety reasons"? */
if (ap->flags & ATA_FLAG_PIO_DMA) {
- tf.protocol = ATA_PROT_ATAPI_DMA;
+ tf.protocol = ATAPI_PROT_DMA;
tf.feature |= ATAPI_PKT_DMA;
} else {
- tf.protocol = ATA_PROT_ATAPI;
+ tf.protocol = ATAPI_PROT_PIO;
tf.lbam = SCSI_SENSE_BUFFERSIZE;
tf.lbah = 0;
}
@@ -1451,20 +1489,29 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
return action;
}
-static int ata_eh_categorize_error(int is_io, unsigned int err_mask)
+static int ata_eh_categorize_error(unsigned int eflags, unsigned int err_mask,
+ int *xfer_ok)
{
+ int base = 0;
+
+ if (!(eflags & ATA_EFLAG_DUBIOUS_XFER))
+ *xfer_ok = 1;
+
+ if (!*xfer_ok)
+ base = ATA_ECAT_DUBIOUS_NONE;
+
if (err_mask & AC_ERR_ATA_BUS)
- return 1;
+ return base + ATA_ECAT_ATA_BUS;
if (err_mask & AC_ERR_TIMEOUT)
- return 2;
+ return base + ATA_ECAT_TOUT_HSM;
- if (is_io) {
+ if (eflags & ATA_EFLAG_IS_IO) {
if (err_mask & AC_ERR_HSM)
- return 2;
+ return base + ATA_ECAT_TOUT_HSM;
if ((err_mask &
(AC_ERR_DEV|AC_ERR_MEDIA|AC_ERR_INVALID)) == AC_ERR_DEV)
- return 3;
+ return base + ATA_ECAT_UNK_DEV;
}
return 0;
@@ -1472,18 +1519,22 @@ static int ata_eh_categorize_error(int is_io, unsigned int err_mask)
struct speed_down_verdict_arg {
u64 since;
- int nr_errors[4];
+ int xfer_ok;
+ int nr_errors[ATA_ECAT_NR];
};
static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg)
{
struct speed_down_verdict_arg *arg = void_arg;
- int cat = ata_eh_categorize_error(ent->is_io, ent->err_mask);
+ int cat;
if (ent->timestamp < arg->since)
return -1;
+ cat = ata_eh_categorize_error(ent->eflags, ent->err_mask,
+ &arg->xfer_ok);
arg->nr_errors[cat]++;
+
return 0;
}
@@ -1495,22 +1546,48 @@ static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg)
* whether NCQ needs to be turned off, transfer speed should be
* stepped down, or falling back to PIO is necessary.
*
- * Cat-1 is ATA_BUS error for any command.
+ * ECAT_ATA_BUS : ATA_BUS error for any command
+ *
+ * ECAT_TOUT_HSM : TIMEOUT for any command or HSM violation for
+ * IO commands
+ *
+ * ECAT_UNK_DEV : Unknown DEV error for IO commands
+ *
+ * ECAT_DUBIOUS_* : Identical to above three but occurred while
+ * data transfer hasn't been verified.
+ *
+ * Verdicts are
+ *
+ * NCQ_OFF : Turn off NCQ.
+ *
+ * SPEED_DOWN : Speed down transfer speed but don't fall back
+ * to PIO.
+ *
+ * FALLBACK_TO_PIO : Fall back to PIO.
+ *
+ * Even if multiple verdicts are returned, only one action is
+ * taken per error. An action triggered by non-DUBIOUS errors
+ * clears ering, while one triggered by DUBIOUS_* errors doesn't.
+ * This is to expedite speed down decisions right after device is
+ * initially configured.
*
- * Cat-2 is TIMEOUT for any command or HSM violation for known
- * supported commands.
+ * The followings are speed down rules. #1 and #2 deal with
+ * DUBIOUS errors.
*
- * Cat-3 is is unclassified DEV error for known supported
- * command.
+ * 1. If more than one DUBIOUS_ATA_BUS or DUBIOUS_TOUT_HSM errors
+ * occurred during last 5 mins, SPEED_DOWN and FALLBACK_TO_PIO.
*
- * NCQ needs to be turned off if there have been more than 3
- * Cat-2 + Cat-3 errors during last 10 minutes.
+ * 2. If more than one DUBIOUS_TOUT_HSM or DUBIOUS_UNK_DEV errors
+ * occurred during last 5 mins, NCQ_OFF.
*
- * Speed down is necessary if there have been more than 3 Cat-1 +
- * Cat-2 errors or 10 Cat-3 errors during last 10 minutes.
+ * 3. If more than 8 ATA_BUS, TOUT_HSM or UNK_DEV errors
+ * ocurred during last 5 mins, FALLBACK_TO_PIO
*
- * Falling back to PIO mode is necessary if there have been more
- * than 10 Cat-1 + Cat-2 + Cat-3 errors during last 5 minutes.
+ * 4. If more than 3 TOUT_HSM or UNK_DEV errors occurred
+ * during last 10 mins, NCQ_OFF.
+ *
+ * 5. If more than 3 ATA_BUS or TOUT_HSM errors, or more than 6
+ * UNK_DEV errors occurred during last 10 mins, SPEED_DOWN.
*
* LOCKING:
* Inherited from caller.
@@ -1525,23 +1602,38 @@ static unsigned int ata_eh_speed_down_verdict(struct ata_device *dev)
struct speed_down_verdict_arg arg;
unsigned int verdict = 0;
- /* scan past 10 mins of error history */
+ /* scan past 5 mins of error history */
memset(&arg, 0, sizeof(arg));
- arg.since = j64 - min(j64, j10mins);
+ arg.since = j64 - min(j64, j5mins);
ata_ering_map(&dev->ering, speed_down_verdict_cb, &arg);
- if (arg.nr_errors[2] + arg.nr_errors[3] > 3)
- verdict |= ATA_EH_SPDN_NCQ_OFF;
- if (arg.nr_errors[1] + arg.nr_errors[2] > 3 || arg.nr_errors[3] > 10)
- verdict |= ATA_EH_SPDN_SPEED_DOWN;
+ if (arg.nr_errors[ATA_ECAT_DUBIOUS_ATA_BUS] +
+ arg.nr_errors[ATA_ECAT_DUBIOUS_TOUT_HSM] > 1)
+ verdict |= ATA_EH_SPDN_SPEED_DOWN |
+ ATA_EH_SPDN_FALLBACK_TO_PIO | ATA_EH_SPDN_KEEP_ERRORS;
- /* scan past 3 mins of error history */
+ if (arg.nr_errors[ATA_ECAT_DUBIOUS_TOUT_HSM] +
+ arg.nr_errors[ATA_ECAT_DUBIOUS_UNK_DEV] > 1)
+ verdict |= ATA_EH_SPDN_NCQ_OFF | ATA_EH_SPDN_KEEP_ERRORS;
+
+ if (arg.nr_errors[ATA_ECAT_ATA_BUS] +
+ arg.nr_errors[ATA_ECAT_TOUT_HSM] +
+ arg.nr_errors[ATA_ECAT_UNK_DEV] > 6)
+ verdict |= ATA_EH_SPDN_FALLBACK_TO_PIO;
+
+ /* scan past 10 mins of error history */
memset(&arg, 0, sizeof(arg));
- arg.since = j64 - min(j64, j5mins);
+ arg.since = j64 - min(j64, j10mins);
ata_ering_map(&dev->ering, speed_down_verdict_cb, &arg);
- if (arg.nr_errors[1] + arg.nr_errors[2] + arg.nr_errors[3] > 10)
- verdict |= ATA_EH_SPDN_FALLBACK_TO_PIO;
+ if (arg.nr_errors[ATA_ECAT_TOUT_HSM] +
+ arg.nr_errors[ATA_ECAT_UNK_DEV] > 3)
+ verdict |= ATA_EH_SPDN_NCQ_OFF;
+
+ if (arg.nr_errors[ATA_ECAT_ATA_BUS] +
+ arg.nr_errors[ATA_ECAT_TOUT_HSM] > 3 ||
+ arg.nr_errors[ATA_ECAT_UNK_DEV] > 6)
+ verdict |= ATA_EH_SPDN_SPEED_DOWN;
return verdict;
}
@@ -1549,7 +1641,7 @@ static unsigned int ata_eh_speed_down_verdict(struct ata_device *dev)
/**
* ata_eh_speed_down - record error and speed down if necessary
* @dev: Failed device
- * @is_io: Did the device fail during normal IO?
+ * @eflags: mask of ATA_EFLAG_* flags
* @err_mask: err_mask of the error
*
* Record error and examine error history to determine whether
@@ -1563,18 +1655,20 @@ static unsigned int ata_eh_speed_down_verdict(struct ata_device *dev)
* RETURNS:
* Determined recovery action.
*/
-static unsigned int ata_eh_speed_down(struct ata_device *dev, int is_io,
- unsigned int err_mask)
+static unsigned int ata_eh_speed_down(struct ata_device *dev,
+ unsigned int eflags, unsigned int err_mask)
{
+ struct ata_link *link = dev->link;
+ int xfer_ok = 0;
unsigned int verdict;
unsigned int action = 0;
/* don't bother if Cat-0 error */
- if (ata_eh_categorize_error(is_io, err_mask) == 0)
+ if (ata_eh_categorize_error(eflags, err_mask, &xfer_ok) == 0)
return 0;
/* record error and determine whether speed down is necessary */
- ata_ering_record(&dev->ering, is_io, err_mask);
+ ata_ering_record(&dev->ering, eflags, err_mask);
verdict = ata_eh_speed_down_verdict(dev);
/* turn off NCQ? */
@@ -1590,7 +1684,7 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev, int is_io,
/* speed down? */
if (verdict & ATA_EH_SPDN_SPEED_DOWN) {
/* speed down SATA link speed if possible */
- if (sata_down_spd_limit(dev->link) == 0) {
+ if (sata_down_spd_limit(link) == 0) {
action |= ATA_EH_HARDRESET;
goto done;
}
@@ -1618,10 +1712,10 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev, int is_io,
}
/* Fall back to PIO? Slowing down to PIO is meaningless for
- * SATA. Consider it only for PATA.
+ * SATA ATA devices. Consider it only for PATA and SATAPI.
*/
if ((verdict & ATA_EH_SPDN_FALLBACK_TO_PIO) && (dev->spdn_cnt >= 2) &&
- (dev->link->ap->cbl != ATA_CBL_SATA) &&
+ (link->ap->cbl != ATA_CBL_SATA || dev->class == ATA_DEV_ATAPI) &&
(dev->xfer_shift != ATA_SHIFT_PIO)) {
if (ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO) == 0) {
dev->spdn_cnt = 0;
@@ -1633,7 +1727,8 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev, int is_io,
return 0;
done:
/* device has been slowed down, blow error history */
- ata_ering_clear(&dev->ering);
+ if (!(verdict & ATA_EH_SPDN_KEEP_ERRORS))
+ ata_ering_clear(&dev->ering);
return action;
}
@@ -1653,8 +1748,8 @@ static void ata_eh_link_autopsy(struct ata_link *link)
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
- unsigned int all_err_mask = 0;
- int tag, is_io = 0;
+ unsigned int all_err_mask = 0, eflags = 0;
+ int tag;
u32 serror;
int rc;
@@ -1713,15 +1808,15 @@ static void ata_eh_link_autopsy(struct ata_link *link)
ehc->i.dev = qc->dev;
all_err_mask |= qc->err_mask;
if (qc->flags & ATA_QCFLAG_IO)
- is_io = 1;
+ eflags |= ATA_EFLAG_IS_IO;
}
/* enforce default EH actions */
if (ap->pflags & ATA_PFLAG_FROZEN ||
all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT))
ehc->i.action |= ATA_EH_SOFTRESET;
- else if ((is_io && all_err_mask) ||
- (!is_io && (all_err_mask & ~AC_ERR_DEV)))
+ else if (((eflags & ATA_EFLAG_IS_IO) && all_err_mask) ||
+ (!(eflags & ATA_EFLAG_IS_IO) && (all_err_mask & ~AC_ERR_DEV)))
ehc->i.action |= ATA_EH_REVALIDATE;
/* If we have offending qcs and the associated failed device,
@@ -1743,8 +1838,11 @@ static void ata_eh_link_autopsy(struct ata_link *link)
ata_dev_enabled(link->device))))
dev = link->device;
- if (dev)
- ehc->i.action |= ata_eh_speed_down(dev, is_io, all_err_mask);
+ if (dev) {
+ if (dev->flags & ATA_DFLAG_DUBIOUS_XFER)
+ eflags |= ATA_EFLAG_DUBIOUS_XFER;
+ ehc->i.action |= ata_eh_speed_down(dev, eflags, all_err_mask);
+ }
DPRINTK("EXIT\n");
}
@@ -1880,8 +1978,8 @@ static void ata_eh_link_report(struct ata_link *link)
[ATA_PROT_PIO] = "pio",
[ATA_PROT_DMA] = "dma",
[ATA_PROT_NCQ] = "ncq",
- [ATA_PROT_ATAPI] = "pio",
- [ATA_PROT_ATAPI_DMA] = "dma",
+ [ATAPI_PROT_PIO] = "pio",
+ [ATAPI_PROT_DMA] = "dma",
};
snprintf(data_buf, sizeof(data_buf), " %s %u %s",
@@ -1889,7 +1987,7 @@ static void ata_eh_link_report(struct ata_link *link)
dma_str[qc->dma_dir]);
}
- if (is_atapi_taskfile(&qc->tf))
+ if (ata_is_atapi(qc->tf.protocol))
snprintf(cdb_buf, sizeof(cdb_buf),
"cdb %02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x\n ",
@@ -2329,6 +2427,58 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
return rc;
}
+/**
+ * ata_set_mode - Program timings and issue SET FEATURES - XFER
+ * @link: link on which timings will be programmed
+ * @r_failed_dev: out paramter for failed device
+ *
+ * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If
+ * ata_set_mode() fails, pointer to the failing device is
+ * returned in @r_failed_dev.
+ *
+ * LOCKING:
+ * PCI/etc. bus probe sem.
+ *
+ * RETURNS:
+ * 0 on success, negative errno otherwise
+ */
+int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
+{
+ struct ata_port *ap = link->ap;
+ struct ata_device *dev;
+ int rc;
+
+ /* if data transfer is verified, clear DUBIOUS_XFER on ering top */
+ ata_link_for_each_dev(dev, link) {
+ if (!(dev->flags & ATA_DFLAG_DUBIOUS_XFER)) {
+ struct ata_ering_entry *ent;
+
+ ent = ata_ering_top(&dev->ering);
+ if (ent)
+ ent->eflags &= ~ATA_EFLAG_DUBIOUS_XFER;
+ }
+ }
+
+ /* has private set_mode? */
+ if (ap->ops->set_mode)
+ rc = ap->ops->set_mode(link, r_failed_dev);
+ else
+ rc = ata_do_set_mode(link, r_failed_dev);
+
+ /* if transfer mode has changed, set DUBIOUS_XFER on device */
+ ata_link_for_each_dev(dev, link) {
+ struct ata_eh_context *ehc = &link->eh_context;
+ u8 saved_xfer_mode = ehc->saved_xfer_mode[dev->devno];
+ u8 saved_ncq = !!(ehc->saved_ncq_enabled & (1 << dev->devno));
+
+ if (dev->xfer_mode != saved_xfer_mode ||
+ ata_ncq_enabled(dev) != saved_ncq)
+ dev->flags |= ATA_DFLAG_DUBIOUS_XFER;
+ }
+
+ return rc;
+}
+
static int ata_link_nr_enabled(struct ata_link *link)
{
struct ata_device *dev;
@@ -2375,6 +2525,24 @@ static int ata_eh_skip_recovery(struct ata_link *link)
return 1;
}
+static int ata_eh_schedule_probe(struct ata_device *dev)
+{
+ struct ata_eh_context *ehc = &dev->link->eh_context;
+
+ if (!(ehc->i.probe_mask & (1 << dev->devno)) ||
+ (ehc->did_probe_mask & (1 << dev->devno)))
+ return 0;
+
+ ata_eh_detach_dev(dev);
+ ata_dev_init(dev);
+ ehc->did_probe_mask |= (1 << dev->devno);
+ ehc->i.action |= ATA_EH_SOFTRESET;
+ ehc->saved_xfer_mode[dev->devno] = 0;
+ ehc->saved_ncq_enabled &= ~(1 << dev->devno);
+
+ return 1;
+}
+
static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
@@ -2406,16 +2574,9 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
if (ata_link_offline(dev->link))
ata_eh_detach_dev(dev);
- /* probe if requested */
- if ((ehc->i.probe_mask & (1 << dev->devno)) &&
- !(ehc->did_probe_mask & (1 << dev->devno))) {
- ata_eh_detach_dev(dev);
- ata_dev_init(dev);
-
+ /* schedule probe if necessary */
+ if (ata_eh_schedule_probe(dev))
ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
- ehc->did_probe_mask |= (1 << dev->devno);
- ehc->i.action |= ATA_EH_SOFTRESET;
- }
return 1;
} else {
@@ -2492,14 +2653,9 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
if (dev->flags & ATA_DFLAG_DETACH)
ata_eh_detach_dev(dev);
- if (!ata_dev_enabled(dev) &&
- ((ehc->i.probe_mask & (1 << dev->devno)) &&
- !(ehc->did_probe_mask & (1 << dev->devno)))) {
- ata_eh_detach_dev(dev);
- ata_dev_init(dev);
- ehc->did_probe_mask |= (1 << dev->devno);
- ehc->i.action |= ATA_EH_SOFTRESET;
- }
+ /* schedule probe if necessary */
+ if (!ata_dev_enabled(dev))
+ ata_eh_schedule_probe(dev);
}
}
@@ -2747,6 +2903,7 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap)
if (ap->ops->port_suspend)
rc = ap->ops->port_suspend(ap, ap->pm_mesg);
+ ata_acpi_set_state(ap, PMSG_SUSPEND);
out:
/* report result */
spin_lock_irqsave(ap->lock, flags);
@@ -2792,6 +2949,8 @@ static void ata_eh_handle_port_resume(struct ata_port *ap)
WARN_ON(!(ap->pflags & ATA_PFLAG_SUSPENDED));
+ ata_acpi_set_state(ap, PMSG_ON);
+
if (ap->ops->port_resume)
rc = ap->ops->port_resume(ap);
diff --git a/trunk/drivers/ata/libata-scsi.c b/trunk/drivers/ata/libata-scsi.c
index 14daf4848f09..3fd08201bef4 100644
--- a/trunk/drivers/ata/libata-scsi.c
+++ b/trunk/drivers/ata/libata-scsi.c
@@ -517,7 +517,7 @@ static struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev,
qc->scsicmd = cmd;
qc->scsidone = done;
- qc->__sg = scsi_sglist(cmd);
+ qc->sg = scsi_sglist(cmd);
qc->n_elem = scsi_sg_count(cmd);
} else {
cmd->result = (DID_OK << 16) | (QUEUE_FULL << 1);
@@ -2210,7 +2210,7 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
/* sector size */
ATA_SCSI_RBUF_SET(6, ATA_SECT_SIZE >> 8);
- ATA_SCSI_RBUF_SET(7, ATA_SECT_SIZE);
+ ATA_SCSI_RBUF_SET(7, ATA_SECT_SIZE & 0xff);
} else {
/* sector count, 64-bit */
ATA_SCSI_RBUF_SET(0, last_lba >> (8 * 7));
@@ -2224,7 +2224,7 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
/* sector size */
ATA_SCSI_RBUF_SET(10, ATA_SECT_SIZE >> 8);
- ATA_SCSI_RBUF_SET(11, ATA_SECT_SIZE);
+ ATA_SCSI_RBUF_SET(11, ATA_SECT_SIZE & 0xff);
}
return 0;
@@ -2331,7 +2331,7 @@ static void atapi_request_sense(struct ata_queued_cmd *qc)
DPRINTK("ATAPI request sense\n");
/* FIXME: is this needed? */
- memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
ap->ops->tf_read(ap, &qc->tf);
@@ -2341,7 +2341,9 @@ static void atapi_request_sense(struct ata_queued_cmd *qc)
ata_qc_reinit(qc);
- ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer));
+ /* setup sg table and init transfer direction */
+ sg_init_one(&qc->sgent, cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
+ ata_sg_init(qc, &qc->sgent, 1);
qc->dma_dir = DMA_FROM_DEVICE;
memset(&qc->cdb, 0, qc->dev->cdb_len);
@@ -2352,10 +2354,10 @@ static void atapi_request_sense(struct ata_queued_cmd *qc)
qc->tf.command = ATA_CMD_PACKET;
if (ata_pio_use_silly(ap)) {
- qc->tf.protocol = ATA_PROT_ATAPI_DMA;
+ qc->tf.protocol = ATAPI_PROT_DMA;
qc->tf.feature |= ATAPI_PKT_DMA;
} else {
- qc->tf.protocol = ATA_PROT_ATAPI;
+ qc->tf.protocol = ATAPI_PROT_PIO;
qc->tf.lbam = SCSI_SENSE_BUFFERSIZE;
qc->tf.lbah = 0;
}
@@ -2526,12 +2528,12 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
if (using_pio || nodata) {
/* no data, or PIO data xfer */
if (nodata)
- qc->tf.protocol = ATA_PROT_ATAPI_NODATA;
+ qc->tf.protocol = ATAPI_PROT_NODATA;
else
- qc->tf.protocol = ATA_PROT_ATAPI;
+ qc->tf.protocol = ATAPI_PROT_PIO;
} else {
/* DMA data xfer */
- qc->tf.protocol = ATA_PROT_ATAPI_DMA;
+ qc->tf.protocol = ATAPI_PROT_DMA;
qc->tf.feature |= ATAPI_PKT_DMA;
if (atapi_dmadir && (scmd->sc_data_direction != DMA_TO_DEVICE))
@@ -2690,6 +2692,24 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
if ((tf->protocol = ata_scsi_map_proto(cdb[1])) == ATA_PROT_UNKNOWN)
goto invalid_fld;
+ /*
+ * Filter TPM commands by default. These provide an
+ * essentially uncontrolled encrypted "back door" between
+ * applications and the disk. Set libata.allow_tpm=1 if you
+ * have a real reason for wanting to use them. This ensures
+ * that installed software cannot easily mess stuff up without
+ * user intent. DVR type users will probably ship with this enabled
+ * for movie content management.
+ *
+ * Note that for ATA8 we can issue a DCS change and DCS freeze lock
+ * for this and should do in future but that it is not sufficient as
+ * DCS is an optional feature set. Thus we also do the software filter
+ * so that we comply with the TC consortium stated goal that the user
+ * can turn off TC features of their system.
+ */
+ if (tf->command >= 0x5C && tf->command <= 0x5F && !libata_allow_tpm)
+ goto invalid_fld;
+
/* We may not issue DMA commands if no DMA mode is set */
if (tf->protocol == ATA_PROT_DMA && dev->dma_mode == 0)
goto invalid_fld;
diff --git a/trunk/drivers/ata/libata-sff.c b/trunk/drivers/ata/libata-sff.c
index b7ac80b4b1fb..60cd4b179766 100644
--- a/trunk/drivers/ata/libata-sff.c
+++ b/trunk/drivers/ata/libata-sff.c
@@ -147,7 +147,9 @@ void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
* @tf: ATA taskfile register set for storing input
*
* Reads ATA taskfile registers for currently-selected device
- * into @tf.
+ * into @tf. Assumes the device has a fully SFF compliant task file
+ * layout and behaviour. If you device does not (eg has a different
+ * status method) then you will need to provide a replacement tf_read
*
* LOCKING:
* Inherited from caller.
@@ -156,7 +158,7 @@ void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
{
struct ata_ioports *ioaddr = &ap->ioaddr;
- tf->command = ata_chk_status(ap);
+ tf->command = ata_check_status(ap);
tf->feature = ioread8(ioaddr->error_addr);
tf->nsect = ioread8(ioaddr->nsect_addr);
tf->lbal = ioread8(ioaddr->lbal_addr);
@@ -415,7 +417,7 @@ void ata_bmdma_drive_eh(struct ata_port *ap, ata_prereset_fn_t prereset,
ap->hsm_task_state = HSM_ST_IDLE;
if (qc && (qc->tf.protocol == ATA_PROT_DMA ||
- qc->tf.protocol == ATA_PROT_ATAPI_DMA)) {
+ qc->tf.protocol == ATAPI_PROT_DMA)) {
u8 host_stat;
host_stat = ap->ops->bmdma_status(ap);
@@ -549,7 +551,7 @@ int ata_pci_init_bmdma(struct ata_host *host)
return rc;
/* request and iomap DMA region */
- rc = pcim_iomap_regions(pdev, 1 << 4, DRV_NAME);
+ rc = pcim_iomap_regions(pdev, 1 << 4, dev_driver_string(gdev));
if (rc) {
dev_printk(KERN_ERR, gdev, "failed to request/iomap BAR4\n");
return -ENOMEM;
@@ -619,7 +621,8 @@ int ata_pci_init_sff_host(struct ata_host *host)
continue;
}
- rc = pcim_iomap_regions(pdev, 0x3 << base, DRV_NAME);
+ rc = pcim_iomap_regions(pdev, 0x3 << base,
+ dev_driver_string(gdev));
if (rc) {
dev_printk(KERN_WARNING, gdev,
"failed to request/iomap BARs for port %d "
@@ -710,6 +713,99 @@ int ata_pci_prepare_sff_host(struct pci_dev *pdev,
return rc;
}
+/**
+ * ata_pci_activate_sff_host - start SFF host, request IRQ and register it
+ * @host: target SFF ATA host
+ * @irq_handler: irq_handler used when requesting IRQ(s)
+ * @sht: scsi_host_template to use when registering the host
+ *
+ * This is the counterpart of ata_host_activate() for SFF ATA
+ * hosts. This separate helper is necessary because SFF hosts
+ * use two separate interrupts in legacy mode.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ata_pci_activate_sff_host(struct ata_host *host,
+ irq_handler_t irq_handler,
+ struct scsi_host_template *sht)
+{
+ struct device *dev = host->dev;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ const char *drv_name = dev_driver_string(host->dev);
+ int legacy_mode = 0, rc;
+
+ rc = ata_host_start(host);
+ if (rc)
+ return rc;
+
+ if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+ u8 tmp8, mask;
+
+ /* TODO: What if one channel is in native mode ... */
+ pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
+ mask = (1 << 2) | (1 << 0);
+ if ((tmp8 & mask) != mask)
+ legacy_mode = 1;
+#if defined(CONFIG_NO_ATA_LEGACY)
+ /* Some platforms with PCI limits cannot address compat
+ port space. In that case we punt if their firmware has
+ left a device in compatibility mode */
+ if (legacy_mode) {
+ printk(KERN_ERR "ata: Compatibility mode ATA is not supported on this platform, skipping.\n");
+ return -EOPNOTSUPP;
+ }
+#endif
+ }
+
+ if (!devres_open_group(dev, NULL, GFP_KERNEL))
+ return -ENOMEM;
+
+ if (!legacy_mode && pdev->irq) {
+ rc = devm_request_irq(dev, pdev->irq, irq_handler,
+ IRQF_SHARED, drv_name, host);
+ if (rc)
+ goto out;
+
+ ata_port_desc(host->ports[0], "irq %d", pdev->irq);
+ ata_port_desc(host->ports[1], "irq %d", pdev->irq);
+ } else if (legacy_mode) {
+ if (!ata_port_is_dummy(host->ports[0])) {
+ rc = devm_request_irq(dev, ATA_PRIMARY_IRQ(pdev),
+ irq_handler, IRQF_SHARED,
+ drv_name, host);
+ if (rc)
+ goto out;
+
+ ata_port_desc(host->ports[0], "irq %d",
+ ATA_PRIMARY_IRQ(pdev));
+ }
+
+ if (!ata_port_is_dummy(host->ports[1])) {
+ rc = devm_request_irq(dev, ATA_SECONDARY_IRQ(pdev),
+ irq_handler, IRQF_SHARED,
+ drv_name, host);
+ if (rc)
+ goto out;
+
+ ata_port_desc(host->ports[1], "irq %d",
+ ATA_SECONDARY_IRQ(pdev));
+ }
+ }
+
+ rc = ata_host_register(host, sht);
+ out:
+ if (rc == 0)
+ devres_remove_group(dev, NULL);
+ else
+ devres_release_group(dev, NULL);
+
+ return rc;
+}
+
/**
* ata_pci_init_one - Initialize/register PCI IDE host controller
* @pdev: Controller to be initialized
@@ -739,8 +835,6 @@ int ata_pci_init_one(struct pci_dev *pdev,
struct device *dev = &pdev->dev;
const struct ata_port_info *pi = NULL;
struct ata_host *host = NULL;
- u8 mask;
- int legacy_mode = 0;
int i, rc;
DPRINTK("ENTER\n");
@@ -762,95 +856,24 @@ int ata_pci_init_one(struct pci_dev *pdev,
if (!devres_open_group(dev, NULL, GFP_KERNEL))
return -ENOMEM;
- /* FIXME: Really for ATA it isn't safe because the device may be
- multi-purpose and we want to leave it alone if it was already
- enabled. Secondly for shared use as Arjan says we want refcounting
-
- Checking dev->is_enabled is insufficient as this is not set at
- boot for the primary video which is BIOS enabled
- */
-
rc = pcim_enable_device(pdev);
if (rc)
- goto err_out;
+ goto out;
- if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
- u8 tmp8;
-
- /* TODO: What if one channel is in native mode ... */
- pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
- mask = (1 << 2) | (1 << 0);
- if ((tmp8 & mask) != mask)
- legacy_mode = 1;
-#if defined(CONFIG_NO_ATA_LEGACY)
- /* Some platforms with PCI limits cannot address compat
- port space. In that case we punt if their firmware has
- left a device in compatibility mode */
- if (legacy_mode) {
- printk(KERN_ERR "ata: Compatibility mode ATA is not supported on this platform, skipping.\n");
- rc = -EOPNOTSUPP;
- goto err_out;
- }
-#endif
- }
-
- /* prepare host */
+ /* prepare and activate SFF host */
rc = ata_pci_prepare_sff_host(pdev, ppi, &host);
if (rc)
- goto err_out;
+ goto out;
pci_set_master(pdev);
+ rc = ata_pci_activate_sff_host(host, pi->port_ops->irq_handler,
+ pi->sht);
+ out:
+ if (rc == 0)
+ devres_remove_group(&pdev->dev, NULL);
+ else
+ devres_release_group(&pdev->dev, NULL);
- /* start host and request IRQ */
- rc = ata_host_start(host);
- if (rc)
- goto err_out;
-
- if (!legacy_mode && pdev->irq) {
- /* We may have no IRQ assigned in which case we can poll. This
- shouldn't happen on a sane system but robustness is cheap
- in this case */
- rc = devm_request_irq(dev, pdev->irq, pi->port_ops->irq_handler,
- IRQF_SHARED, DRV_NAME, host);
- if (rc)
- goto err_out;
-
- ata_port_desc(host->ports[0], "irq %d", pdev->irq);
- ata_port_desc(host->ports[1], "irq %d", pdev->irq);
- } else if (legacy_mode) {
- if (!ata_port_is_dummy(host->ports[0])) {
- rc = devm_request_irq(dev, ATA_PRIMARY_IRQ(pdev),
- pi->port_ops->irq_handler,
- IRQF_SHARED, DRV_NAME, host);
- if (rc)
- goto err_out;
-
- ata_port_desc(host->ports[0], "irq %d",
- ATA_PRIMARY_IRQ(pdev));
- }
-
- if (!ata_port_is_dummy(host->ports[1])) {
- rc = devm_request_irq(dev, ATA_SECONDARY_IRQ(pdev),
- pi->port_ops->irq_handler,
- IRQF_SHARED, DRV_NAME, host);
- if (rc)
- goto err_out;
-
- ata_port_desc(host->ports[1], "irq %d",
- ATA_SECONDARY_IRQ(pdev));
- }
- }
-
- /* register */
- rc = ata_host_register(host, pi->sht);
- if (rc)
- goto err_out;
-
- devres_remove_group(dev, NULL);
- return 0;
-
-err_out:
- devres_release_group(dev, NULL);
return rc;
}
diff --git a/trunk/drivers/ata/libata.h b/trunk/drivers/ata/libata.h
index bbe59c2fd1e2..409ffb9af163 100644
--- a/trunk/drivers/ata/libata.h
+++ b/trunk/drivers/ata/libata.h
@@ -60,6 +60,7 @@ extern int atapi_dmadir;
extern int atapi_passthru16;
extern int libata_fua;
extern int libata_noacpi;
+extern int libata_allow_tpm;
extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev);
extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
u64 block, u32 n_block, unsigned int tf_flags,
@@ -85,7 +86,6 @@ extern int ata_dev_configure(struct ata_device *dev);
extern int sata_down_spd_limit(struct ata_link *link);
extern int sata_set_spd_needed(struct ata_link *link);
extern int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel);
-extern int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev);
extern void ata_sg_clean(struct ata_queued_cmd *qc);
extern void ata_qc_free(struct ata_queued_cmd *qc);
extern void ata_qc_issue(struct ata_queued_cmd *qc);
@@ -113,6 +113,7 @@ extern int ata_acpi_on_suspend(struct ata_port *ap);
extern void ata_acpi_on_resume(struct ata_port *ap);
extern int ata_acpi_on_devcfg(struct ata_device *dev);
extern void ata_acpi_on_disable(struct ata_device *dev);
+extern void ata_acpi_set_state(struct ata_port *ap, pm_message_t state);
#else
static inline void ata_acpi_associate_sata_port(struct ata_port *ap) { }
static inline void ata_acpi_associate(struct ata_host *host) { }
@@ -121,6 +122,8 @@ static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; }
static inline void ata_acpi_on_resume(struct ata_port *ap) { }
static inline int ata_acpi_on_devcfg(struct ata_device *dev) { return 0; }
static inline void ata_acpi_on_disable(struct ata_device *dev) { }
+static inline void ata_acpi_set_state(struct ata_port *ap,
+ pm_message_t state) { }
#endif
/* libata-scsi.c */
@@ -183,6 +186,7 @@ extern void ata_eh_report(struct ata_port *ap);
extern int ata_eh_reset(struct ata_link *link, int classify,
ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
ata_reset_fn_t hardreset, ata_postreset_fn_t postreset);
+extern int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev);
extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
ata_postreset_fn_t postreset,
diff --git a/trunk/drivers/ata/pata_acpi.c b/trunk/drivers/ata/pata_acpi.c
index e4542ab9c7f8..244098a80ce4 100644
--- a/trunk/drivers/ata/pata_acpi.c
+++ b/trunk/drivers/ata/pata_acpi.c
@@ -81,17 +81,6 @@ static void pacpi_error_handler(struct ata_port *ap)
NULL, ata_std_postreset);
}
-/* Welcome to ACPI, bring a bucket */
-static const unsigned int pio_cycle[7] = {
- 600, 383, 240, 180, 120, 100, 80
-};
-static const unsigned int mwdma_cycle[5] = {
- 480, 150, 120, 100, 80
-};
-static const unsigned int udma_cycle[7] = {
- 120, 80, 60, 45, 30, 20, 15
-};
-
/**
* pacpi_discover_modes - filter non ACPI modes
* @adev: ATA device
@@ -103,56 +92,20 @@ static const unsigned int udma_cycle[7] = {
static unsigned long pacpi_discover_modes(struct ata_port *ap, struct ata_device *adev)
{
- int unit = adev->devno;
struct pata_acpi *acpi = ap->private_data;
- int i;
- u32 t;
- unsigned long mask = (0x7f << ATA_SHIFT_UDMA) | (0x7 << ATA_SHIFT_MWDMA) | (0x1F << ATA_SHIFT_PIO);
-
struct ata_acpi_gtm probe;
+ unsigned int xfer_mask;
probe = acpi->gtm;
- /* We always use the 0 slot for crap hardware */
- if (!(probe.flags & 0x10))
- unit = 0;
-
ata_acpi_gtm(ap, &probe);
- /* Start by scanning for PIO modes */
- for (i = 0; i < 7; i++) {
- t = probe.drive[unit].pio;
- if (t <= pio_cycle[i]) {
- mask |= (2 << (ATA_SHIFT_PIO + i)) - 1;
- break;
- }
- }
+ xfer_mask = ata_acpi_gtm_xfermask(adev, &probe);
- /* See if we have MWDMA or UDMA data. We don't bother with MWDMA
- if UDMA is availabe as this means the BIOS set UDMA and our
- error changedown if it works is UDMA to PIO anyway */
- if (probe.flags & (1 << (2 * unit))) {
- /* MWDMA */
- for (i = 0; i < 5; i++) {
- t = probe.drive[unit].dma;
- if (t <= mwdma_cycle[i]) {
- mask |= (2 << (ATA_SHIFT_MWDMA + i)) - 1;
- break;
- }
- }
- } else {
- /* UDMA */
- for (i = 0; i < 7; i++) {
- t = probe.drive[unit].dma;
- if (t <= udma_cycle[i]) {
- mask |= (2 << (ATA_SHIFT_UDMA + i)) - 1;
- break;
- }
- }
- }
- if (mask & (0xF8 << ATA_SHIFT_UDMA))
+ if (xfer_mask & (0xF8 << ATA_SHIFT_UDMA))
ap->cbl = ATA_CBL_PATA80;
- return mask;
+
+ return xfer_mask;
}
/**
@@ -180,12 +133,14 @@ static void pacpi_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
int unit = adev->devno;
struct pata_acpi *acpi = ap->private_data;
+ const struct ata_timing *t;
if (!(acpi->gtm.flags & 0x10))
unit = 0;
/* Now stuff the nS values into the structure */
- acpi->gtm.drive[unit].pio = pio_cycle[adev->pio_mode - XFER_PIO_0];
+ t = ata_timing_find_mode(adev->pio_mode);
+ acpi->gtm.drive[unit].pio = t->cycle;
ata_acpi_stm(ap, &acpi->gtm);
/* See what mode we actually got */
ata_acpi_gtm(ap, &acpi->gtm);
@@ -201,16 +156,18 @@ static void pacpi_set_dmamode(struct ata_port *ap, struct ata_device *adev)
{
int unit = adev->devno;
struct pata_acpi *acpi = ap->private_data;
+ const struct ata_timing *t;
if (!(acpi->gtm.flags & 0x10))
unit = 0;
/* Now stuff the nS values into the structure */
+ t = ata_timing_find_mode(adev->dma_mode);
if (adev->dma_mode >= XFER_UDMA_0) {
- acpi->gtm.drive[unit].dma = udma_cycle[adev->dma_mode - XFER_UDMA_0];
+ acpi->gtm.drive[unit].dma = t->udma;
acpi->gtm.flags |= (1 << (2 * unit));
} else {
- acpi->gtm.drive[unit].dma = mwdma_cycle[adev->dma_mode - XFER_MW_DMA_0];
+ acpi->gtm.drive[unit].dma = t->cycle;
acpi->gtm.flags &= ~(1 << (2 * unit));
}
ata_acpi_stm(ap, &acpi->gtm);
diff --git a/trunk/drivers/ata/pata_ali.c b/trunk/drivers/ata/pata_ali.c
index 8caf9afc8b90..7e68edf3c0f3 100644
--- a/trunk/drivers/ata/pata_ali.c
+++ b/trunk/drivers/ata/pata_ali.c
@@ -64,7 +64,7 @@ static int ali_cable_override(struct pci_dev *pdev)
if (pdev->subsystem_vendor == 0x10CF && pdev->subsystem_device == 0x10AF)
return 1;
/* Mitac 8317 (Winbook-A) and relatives */
- if (pdev->subsystem_vendor == 0x1071 && pdev->subsystem_device == 0x8317)
+ if (pdev->subsystem_vendor == 0x1071 && pdev->subsystem_device == 0x8317)
return 1;
/* Systems by DMI */
if (dmi_check_system(cable_dmi_table))
diff --git a/trunk/drivers/ata/pata_amd.c b/trunk/drivers/ata/pata_amd.c
index 3cc27b514654..761a66608d7b 100644
--- a/trunk/drivers/ata/pata_amd.c
+++ b/trunk/drivers/ata/pata_amd.c
@@ -220,6 +220,62 @@ static void amd133_set_dmamode(struct ata_port *ap, struct ata_device *adev)
timing_setup(ap, adev, 0x40, adev->dma_mode, 4);
}
+/* Both host-side and drive-side detection results are worthless on NV
+ * PATAs. Ignore them and just follow what BIOS configured. Both the
+ * current configuration in PCI config reg and ACPI GTM result are
+ * cached during driver attach and are consulted to select transfer
+ * mode.
+ */
+static unsigned long nv_mode_filter(struct ata_device *dev,
+ unsigned long xfer_mask)
+{
+ static const unsigned int udma_mask_map[] =
+ { ATA_UDMA2, ATA_UDMA1, ATA_UDMA0, 0,
+ ATA_UDMA3, ATA_UDMA4, ATA_UDMA5, ATA_UDMA6 };
+ struct ata_port *ap = dev->link->ap;
+ char acpi_str[32] = "";
+ u32 saved_udma, udma;
+ const struct ata_acpi_gtm *gtm;
+ unsigned long bios_limit = 0, acpi_limit = 0, limit;
+
+ /* find out what BIOS configured */
+ udma = saved_udma = (unsigned long)ap->host->private_data;
+
+ if (ap->port_no == 0)
+ udma >>= 16;
+ if (dev->devno == 0)
+ udma >>= 8;
+
+ if ((udma & 0xc0) == 0xc0)
+ bios_limit = ata_pack_xfermask(0, 0, udma_mask_map[udma & 0x7]);
+
+ /* consult ACPI GTM too */
+ gtm = ata_acpi_init_gtm(ap);
+ if (gtm) {
+ acpi_limit = ata_acpi_gtm_xfermask(dev, gtm);
+
+ snprintf(acpi_str, sizeof(acpi_str), " (%u:%u:0x%x)",
+ gtm->drive[0].dma, gtm->drive[1].dma, gtm->flags);
+ }
+
+ /* be optimistic, EH can take care of things if something goes wrong */
+ limit = bios_limit | acpi_limit;
+
+ /* If PIO or DMA isn't configured at all, don't limit. Let EH
+ * handle it.
+ */
+ if (!(limit & ATA_MASK_PIO))
+ limit |= ATA_MASK_PIO;
+ if (!(limit & (ATA_MASK_MWDMA | ATA_MASK_UDMA)))
+ limit |= ATA_MASK_MWDMA | ATA_MASK_UDMA;
+
+ ata_port_printk(ap, KERN_DEBUG, "nv_mode_filter: 0x%lx&0x%lx->0x%lx, "
+ "BIOS=0x%lx (0x%x) ACPI=0x%lx%s\n",
+ xfer_mask, limit, xfer_mask & limit, bios_limit,
+ saved_udma, acpi_limit, acpi_str);
+
+ return xfer_mask & limit;
+}
/**
* nv_probe_init - cable detection
@@ -252,31 +308,6 @@ static void nv_error_handler(struct ata_port *ap)
ata_std_postreset);
}
-static int nv_cable_detect(struct ata_port *ap)
-{
- static const u8 bitmask[2] = {0x03, 0x0C};
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 ata66;
- u16 udma;
- int cbl;
-
- pci_read_config_byte(pdev, 0x52, &ata66);
- if (ata66 & bitmask[ap->port_no])
- cbl = ATA_CBL_PATA80;
- else
- cbl = ATA_CBL_PATA40;
-
- /* We now have to double check because the Nvidia boxes BIOS
- doesn't always set the cable bits but does set mode bits */
- pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma);
- if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400)
- cbl = ATA_CBL_PATA80;
- /* And a triple check across suspend/resume with ACPI around */
- if (ata_acpi_cbl_80wire(ap))
- cbl = ATA_CBL_PATA80;
- return cbl;
-}
-
/**
* nv100_set_piomode - set initial PIO mode data
* @ap: ATA interface
@@ -314,6 +345,14 @@ static void nv133_set_dmamode(struct ata_port *ap, struct ata_device *adev)
timing_setup(ap, adev, 0x50, adev->dma_mode, 4);
}
+static void nv_host_stop(struct ata_host *host)
+{
+ u32 udma = (unsigned long)host->private_data;
+
+ /* restore PCI config register 0x60 */
+ pci_write_config_dword(to_pci_dev(host->dev), 0x60, udma);
+}
+
static struct scsi_host_template amd_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
@@ -478,7 +517,8 @@ static struct ata_port_operations nv100_port_ops = {
.thaw = ata_bmdma_thaw,
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .cable_detect = nv_cable_detect,
+ .cable_detect = ata_cable_ignore,
+ .mode_filter = nv_mode_filter,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -495,6 +535,7 @@ static struct ata_port_operations nv100_port_ops = {
.irq_on = ata_irq_on,
.port_start = ata_sff_port_start,
+ .host_stop = nv_host_stop,
};
static struct ata_port_operations nv133_port_ops = {
@@ -511,7 +552,8 @@ static struct ata_port_operations nv133_port_ops = {
.thaw = ata_bmdma_thaw,
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .cable_detect = nv_cable_detect,
+ .cable_detect = ata_cable_ignore,
+ .mode_filter = nv_mode_filter,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -528,6 +570,7 @@ static struct ata_port_operations nv133_port_ops = {
.irq_on = ata_irq_on,
.port_start = ata_sff_port_start,
+ .host_stop = nv_host_stop,
};
static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -614,7 +657,8 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
.port_ops = &amd100_port_ops
}
};
- const struct ata_port_info *ppi[] = { NULL, NULL };
+ struct ata_port_info pi;
+ const struct ata_port_info *ppi[] = { &pi, NULL };
static int printed_version;
int type = id->driver_data;
u8 fifo;
@@ -628,6 +672,19 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (type == 1 && pdev->revision > 0x7)
type = 2;
+ /* Serenade ? */
+ if (type == 5 && pdev->subsystem_vendor == PCI_VENDOR_ID_AMD &&
+ pdev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE)
+ type = 6; /* UDMA 100 only */
+
+ /*
+ * Okay, type is determined now. Apply type-specific workarounds.
+ */
+ pi = info[type];
+
+ if (type < 3)
+ ata_pci_clear_simplex(pdev);
+
/* Check for AMD7411 */
if (type == 3)
/* FIFO is broken */
@@ -635,16 +692,17 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
else
pci_write_config_byte(pdev, 0x41, fifo | 0xF0);
- /* Serenade ? */
- if (type == 5 && pdev->subsystem_vendor == PCI_VENDOR_ID_AMD &&
- pdev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE)
- type = 6; /* UDMA 100 only */
+ /* Cable detection on Nvidia chips doesn't work too well,
+ * cache BIOS programmed UDMA mode.
+ */
+ if (type == 7 || type == 8) {
+ u32 udma;
- if (type < 3)
- ata_pci_clear_simplex(pdev);
+ pci_read_config_dword(pdev, 0x60, &udma);
+ pi.private_data = (void *)(unsigned long)udma;
+ }
/* And fire it up */
- ppi[0] = &info[type];
return ata_pci_init_one(pdev, ppi);
}
diff --git a/trunk/drivers/ata/pata_bf54x.c b/trunk/drivers/ata/pata_bf54x.c
index 7842cc487359..a32e3c44a606 100644
--- a/trunk/drivers/ata/pata_bf54x.c
+++ b/trunk/drivers/ata/pata_bf54x.c
@@ -832,6 +832,7 @@ static void bfin_bmdma_setup(struct ata_queued_cmd *qc)
{
unsigned short config = WDSIZE_16;
struct scatterlist *sg;
+ unsigned int si;
pr_debug("in atapi dma setup\n");
/* Program the ATA_CTRL register with dir */
@@ -839,7 +840,7 @@ static void bfin_bmdma_setup(struct ata_queued_cmd *qc)
/* fill the ATAPI DMA controller */
set_dma_config(CH_ATAPI_TX, config);
set_dma_x_modify(CH_ATAPI_TX, 2);
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
set_dma_start_addr(CH_ATAPI_TX, sg_dma_address(sg));
set_dma_x_count(CH_ATAPI_TX, sg_dma_len(sg) >> 1);
}
@@ -848,7 +849,7 @@ static void bfin_bmdma_setup(struct ata_queued_cmd *qc)
/* fill the ATAPI DMA controller */
set_dma_config(CH_ATAPI_RX, config);
set_dma_x_modify(CH_ATAPI_RX, 2);
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
set_dma_start_addr(CH_ATAPI_RX, sg_dma_address(sg));
set_dma_x_count(CH_ATAPI_RX, sg_dma_len(sg) >> 1);
}
@@ -867,6 +868,7 @@ static void bfin_bmdma_start(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap;
void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
struct scatterlist *sg;
+ unsigned int si;
pr_debug("in atapi dma start\n");
if (!(ap->udma_mask || ap->mwdma_mask))
@@ -881,7 +883,7 @@ static void bfin_bmdma_start(struct ata_queued_cmd *qc)
* data cache is enabled. Otherwise, this loop
* is an empty loop and optimized out.
*/
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
flush_dcache_range(sg_dma_address(sg),
sg_dma_address(sg) + sg_dma_len(sg));
}
@@ -910,7 +912,7 @@ static void bfin_bmdma_start(struct ata_queued_cmd *qc)
ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) | TFRCNT_RST);
/* Set transfer length to buffer len */
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
ATAPI_SET_XFER_LEN(base, (sg_dma_len(sg) >> 1));
}
@@ -932,6 +934,7 @@ static void bfin_bmdma_stop(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct scatterlist *sg;
+ unsigned int si;
pr_debug("in atapi dma stop\n");
if (!(ap->udma_mask || ap->mwdma_mask))
@@ -950,7 +953,7 @@ static void bfin_bmdma_stop(struct ata_queued_cmd *qc)
* data cache is enabled. Otherwise, this loop
* is an empty loop and optimized out.
*/
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
invalidate_dcache_range(
sg_dma_address(sg),
sg_dma_address(sg)
@@ -1167,34 +1170,36 @@ static unsigned char bfin_bmdma_status(struct ata_port *ap)
* Note: Original code is ata_data_xfer().
*/
-static void bfin_data_xfer(struct ata_device *adev, unsigned char *buf,
- unsigned int buflen, int write_data)
+static unsigned int bfin_data_xfer(struct ata_device *dev, unsigned char *buf,
+ unsigned int buflen, int rw)
{
- struct ata_port *ap = adev->link->ap;
- unsigned int words = buflen >> 1;
- unsigned short *buf16 = (u16 *) buf;
+ struct ata_port *ap = dev->link->ap;
void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
+ unsigned int words = buflen >> 1;
+ unsigned short *buf16 = (u16 *)buf;
/* Transfer multiple of 2 bytes */
- if (write_data) {
- write_atapi_data(base, words, buf16);
- } else {
+ if (rw == READ)
read_atapi_data(base, words, buf16);
- }
+ else
+ write_atapi_data(base, words, buf16);
/* Transfer trailing 1 byte, if any. */
if (unlikely(buflen & 0x01)) {
unsigned short align_buf[1] = { 0 };
unsigned char *trailing_buf = buf + buflen - 1;
- if (write_data) {
- memcpy(align_buf, trailing_buf, 1);
- write_atapi_data(base, 1, align_buf);
- } else {
+ if (rw == READ) {
read_atapi_data(base, 1, align_buf);
memcpy(trailing_buf, align_buf, 1);
+ } else {
+ memcpy(align_buf, trailing_buf, 1);
+ write_atapi_data(base, 1, align_buf);
}
+ words++;
}
+
+ return words << 1;
}
/**
diff --git a/trunk/drivers/ata/pata_cs5520.c b/trunk/drivers/ata/pata_cs5520.c
index 33f7f0843f4f..d4590f546c49 100644
--- a/trunk/drivers/ata/pata_cs5520.c
+++ b/trunk/drivers/ata/pata_cs5520.c
@@ -198,7 +198,7 @@ static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_devi
};
const struct ata_port_info *ppi[2];
u8 pcicfg;
- void *iomap[5];
+ void __iomem *iomap[5];
struct ata_host *host;
struct ata_ioports *ioaddr;
int i, rc;
diff --git a/trunk/drivers/ata/pata_hpt37x.c b/trunk/drivers/ata/pata_hpt37x.c
index c79f066c2bc9..68eb34929cec 100644
--- a/trunk/drivers/ata/pata_hpt37x.c
+++ b/trunk/drivers/ata/pata_hpt37x.c
@@ -847,15 +847,16 @@ static u32 hpt374_read_freq(struct pci_dev *pdev)
u32 freq;
unsigned long io_base = pci_resource_start(pdev, 4);
if (PCI_FUNC(pdev->devfn) & 1) {
- struct pci_dev *pdev_0 = pci_get_slot(pdev->bus, pdev->devfn - 1);
+ struct pci_dev *pdev_0;
+
+ pdev_0 = pci_get_slot(pdev->bus, pdev->devfn - 1);
/* Someone hot plugged the controller on us ? */
if (pdev_0 == NULL)
return 0;
io_base = pci_resource_start(pdev_0, 4);
freq = inl(io_base + 0x90);
pci_dev_put(pdev_0);
- }
- else
+ } else
freq = inl(io_base + 0x90);
return freq;
}
diff --git a/trunk/drivers/ata/pata_icside.c b/trunk/drivers/ata/pata_icside.c
index 842fe08a3c13..5b8586dac63b 100644
--- a/trunk/drivers/ata/pata_icside.c
+++ b/trunk/drivers/ata/pata_icside.c
@@ -224,6 +224,7 @@ static void pata_icside_bmdma_setup(struct ata_queued_cmd *qc)
struct pata_icside_state *state = ap->host->private_data;
struct scatterlist *sg, *rsg = state->sg;
unsigned int write = qc->tf.flags & ATA_TFLAG_WRITE;
+ unsigned int si;
/*
* We are simplex; BUG if we try to fiddle with DMA
@@ -234,7 +235,7 @@ static void pata_icside_bmdma_setup(struct ata_queued_cmd *qc)
/*
* Copy ATAs scattered sg list into a contiguous array of sg
*/
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
memcpy(rsg, sg, sizeof(*sg));
rsg++;
}
diff --git a/trunk/drivers/ata/pata_it821x.c b/trunk/drivers/ata/pata_it821x.c
index ca9aae09daed..109ddd42c266 100644
--- a/trunk/drivers/ata/pata_it821x.c
+++ b/trunk/drivers/ata/pata_it821x.c
@@ -430,7 +430,7 @@ static unsigned int it821x_smart_qc_issue_prot(struct ata_queued_cmd *qc)
return ata_qc_issue_prot(qc);
}
printk(KERN_DEBUG "it821x: can't process command 0x%02X\n", qc->tf.command);
- return AC_ERR_INVALID;
+ return AC_ERR_DEV;
}
/**
@@ -516,6 +516,37 @@ static void it821x_dev_config(struct ata_device *adev)
printk("(%dK stripe)", adev->id[146]);
printk(".\n");
}
+ /* This is a controller firmware triggered funny, don't
+ report the drive faulty! */
+ adev->horkage &= ~ATA_HORKAGE_DIAGNOSTIC;
+}
+
+/**
+ * it821x_ident_hack - Hack identify data up
+ * @ap: Port
+ *
+ * Walk the devices on this firmware driven port and slightly
+ * mash the identify data to stop us and common tools trying to
+ * use features not firmware supported. The firmware itself does
+ * some masking (eg SMART) but not enough.
+ *
+ * This is a bit of an abuse of the cable method, but it is the
+ * only method called at the right time. We could modify the libata
+ * core specifically for ident hacking but while we have one offender
+ * it seems better to keep the fallout localised.
+ */
+
+static int it821x_ident_hack(struct ata_port *ap)
+{
+ struct ata_device *adev;
+ ata_link_for_each_dev(adev, &ap->link) {
+ if (ata_dev_enabled(adev)) {
+ adev->id[84] &= ~(1 << 6); /* No FUA */
+ adev->id[85] &= ~(1 << 10); /* No HPA */
+ adev->id[76] = 0; /* No NCQ/AN etc */
+ }
+ }
+ return ata_cable_unknown(ap);
}
@@ -634,7 +665,7 @@ static struct ata_port_operations it821x_smart_port_ops = {
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .cable_detect = ata_cable_unknown,
+ .cable_detect = it821x_ident_hack,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/trunk/drivers/ata/pata_ixp4xx_cf.c b/trunk/drivers/ata/pata_ixp4xx_cf.c
index 120b5bfa7ce6..030878fedeb5 100644
--- a/trunk/drivers/ata/pata_ixp4xx_cf.c
+++ b/trunk/drivers/ata/pata_ixp4xx_cf.c
@@ -42,13 +42,13 @@ static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error)
return 0;
}
-static void ixp4xx_mmio_data_xfer(struct ata_device *adev, unsigned char *buf,
- unsigned int buflen, int write_data)
+static unsigned int ixp4xx_mmio_data_xfer(struct ata_device *dev,
+ unsigned char *buf, unsigned int buflen, int rw)
{
unsigned int i;
unsigned int words = buflen >> 1;
u16 *buf16 = (u16 *) buf;
- struct ata_port *ap = adev->link->ap;
+ struct ata_port *ap = dev->link->ap;
void __iomem *mmio = ap->ioaddr.data_addr;
struct ixp4xx_pata_data *data = ap->host->dev->platform_data;
@@ -59,30 +59,32 @@ static void ixp4xx_mmio_data_xfer(struct ata_device *adev, unsigned char *buf,
udelay(100);
/* Transfer multiple of 2 bytes */
- if (write_data) {
- for (i = 0; i < words; i++)
- writew(buf16[i], mmio);
- } else {
+ if (rw == READ)
for (i = 0; i < words; i++)
buf16[i] = readw(mmio);
- }
+ else
+ for (i = 0; i < words; i++)
+ writew(buf16[i], mmio);
/* Transfer trailing 1 byte, if any. */
if (unlikely(buflen & 0x01)) {
u16 align_buf[1] = { 0 };
unsigned char *trailing_buf = buf + buflen - 1;
- if (write_data) {
- memcpy(align_buf, trailing_buf, 1);
- writew(align_buf[0], mmio);
- } else {
+ if (rw == READ) {
align_buf[0] = readw(mmio);
memcpy(trailing_buf, align_buf, 1);
+ } else {
+ memcpy(align_buf, trailing_buf, 1);
+ writew(align_buf[0], mmio);
}
+ words++;
}
udelay(100);
*data->cs0_cfg |= 0x01;
+
+ return words << 1;
}
static struct scsi_host_template ixp4xx_sht = {
diff --git a/trunk/drivers/ata/pata_legacy.c b/trunk/drivers/ata/pata_legacy.c
index 17159b5e1e43..333dc15f8ccf 100644
--- a/trunk/drivers/ata/pata_legacy.c
+++ b/trunk/drivers/ata/pata_legacy.c
@@ -28,7 +28,6 @@
*
* Unsupported but docs exist:
* Appian/Adaptec AIC25VL01/Cirrus Logic PD7220
- * Winbond W83759A
*
* This driver handles legacy (that is "ISA/VLB side") IDE ports found
* on PC class systems. There are three hybrid devices that are exceptions
@@ -36,7 +35,7 @@
* the MPIIX where the tuning is PCI side but the IDE is "ISA side".
*
* Specific support is included for the ht6560a/ht6560b/opti82c611a/
- * opti82c465mv/promise 20230c/20630
+ * opti82c465mv/promise 20230c/20630/winbond83759A
*
* Use the autospeed and pio_mask options with:
* Appian ADI/2 aka CLPD7220 or AIC25VL01.
@@ -47,9 +46,6 @@
* For now use autospeed and pio_mask as above with the W83759A. This may
* change.
*
- * TODO
- * Merge existing pata_qdi driver
- *
*/
#include
@@ -64,12 +60,13 @@
#include
#define DRV_NAME "pata_legacy"
-#define DRV_VERSION "0.5.5"
+#define DRV_VERSION "0.6.5"
#define NR_HOST 6
-static int legacy_port[NR_HOST] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 };
-static int legacy_irq[NR_HOST] = { 14, 15, 11, 10, 8, 12 };
+static int all;
+module_param(all, int, 0444);
+MODULE_PARM_DESC(all, "Grab all legacy port devices, even if PCI(0=off, 1=on)");
struct legacy_data {
unsigned long timing;
@@ -80,20 +77,106 @@ struct legacy_data {
};
+enum controller {
+ BIOS = 0,
+ SNOOP = 1,
+ PDC20230 = 2,
+ HT6560A = 3,
+ HT6560B = 4,
+ OPTI611A = 5,
+ OPTI46X = 6,
+ QDI6500 = 7,
+ QDI6580 = 8,
+ QDI6580DP = 9, /* Dual channel mode is different */
+ W83759A = 10,
+
+ UNKNOWN = -1
+};
+
+
+struct legacy_probe {
+ unsigned char *name;
+ unsigned long port;
+ unsigned int irq;
+ unsigned int slot;
+ enum controller type;
+ unsigned long private;
+};
+
+struct legacy_controller {
+ const char *name;
+ struct ata_port_operations *ops;
+ unsigned int pio_mask;
+ unsigned int flags;
+ int (*setup)(struct platform_device *, struct legacy_probe *probe,
+ struct legacy_data *data);
+};
+
+static int legacy_port[NR_HOST] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 };
+
+static struct legacy_probe probe_list[NR_HOST];
static struct legacy_data legacy_data[NR_HOST];
static struct ata_host *legacy_host[NR_HOST];
static int nr_legacy_host;
-static int probe_all; /* Set to check all ISA port ranges */
-static int ht6560a; /* HT 6560A on primary 1, secondary 2, both 3 */
-static int ht6560b; /* HT 6560A on primary 1, secondary 2, both 3 */
-static int opti82c611a; /* Opti82c611A on primary 1, secondary 2, both 3 */
-static int opti82c46x; /* Opti 82c465MV present (pri/sec autodetect) */
-static int autospeed; /* Chip present which snoops speed changes */
-static int pio_mask = 0x1F; /* PIO range for autospeed devices */
+static int probe_all; /* Set to check all ISA port ranges */
+static int ht6560a; /* HT 6560A on primary 1, second 2, both 3 */
+static int ht6560b; /* HT 6560A on primary 1, second 2, both 3 */
+static int opti82c611a; /* Opti82c611A on primary 1, sec 2, both 3 */
+static int opti82c46x; /* Opti 82c465MV present(pri/sec autodetect) */
+static int qdi; /* Set to probe QDI controllers */
+static int winbond; /* Set to probe Winbond controllers,
+ give I/O port if non stdanard */
+static int autospeed; /* Chip present which snoops speed changes */
+static int pio_mask = 0x1F; /* PIO range for autospeed devices */
static int iordy_mask = 0xFFFFFFFF; /* Use iordy if available */
+/**
+ * legacy_probe_add - Add interface to probe list
+ * @port: Controller port
+ * @irq: IRQ number
+ * @type: Controller type
+ * @private: Controller specific info
+ *
+ * Add an entry into the probe list for ATA controllers. This is used
+ * to add the default ISA slots and then to build up the table
+ * further according to other ISA/VLB/Weird device scans
+ *
+ * An I/O port list is used to keep ordering stable and sane, as we
+ * don't have any good way to talk about ordering otherwise
+ */
+
+static int legacy_probe_add(unsigned long port, unsigned int irq,
+ enum controller type, unsigned long private)
+{
+ struct legacy_probe *lp = &probe_list[0];
+ int i;
+ struct legacy_probe *free = NULL;
+
+ for (i = 0; i < NR_HOST; i++) {
+ if (lp->port == 0 && free == NULL)
+ free = lp;
+ /* Matching port, or the correct slot for ordering */
+ if (lp->port == port || legacy_port[i] == port) {
+ free = lp;
+ break;
+ }
+ lp++;
+ }
+ if (free == NULL) {
+ printk(KERN_ERR "pata_legacy: Too many interfaces.\n");
+ return -1;
+ }
+ /* Fill in the entry for later probing */
+ free->port = port;
+ free->irq = irq;
+ free->type = type;
+ free->private = private;
+ return 0;
+}
+
+
/**
* legacy_set_mode - mode setting
* @link: IDE link
@@ -113,7 +196,8 @@ static int legacy_set_mode(struct ata_link *link, struct ata_device **unused)
ata_link_for_each_dev(dev, link) {
if (ata_dev_enabled(dev)) {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ ata_dev_printk(dev, KERN_INFO,
+ "configured for PIO\n");
dev->pio_mode = XFER_PIO_0;
dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
@@ -171,7 +255,7 @@ static struct ata_port_operations simple_port_ops = {
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
- .port_start = ata_port_start,
+ .port_start = ata_sff_port_start,
};
static struct ata_port_operations legacy_port_ops = {
@@ -198,15 +282,16 @@ static struct ata_port_operations legacy_port_ops = {
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
- .port_start = ata_port_start,
+ .port_start = ata_sff_port_start,
};
/*
* Promise 20230C and 20620 support
*
- * This controller supports PIO0 to PIO2. We set PIO timings conservatively to
- * allow for 50MHz Vesa Local Bus. The 20620 DMA support is weird being DMA to
- * controller and PIO'd to the host and not supported.
+ * This controller supports PIO0 to PIO2. We set PIO timings
+ * conservatively to allow for 50MHz Vesa Local Bus. The 20620 DMA
+ * support is weird being DMA to controller and PIO'd to the host
+ * and not supported.
*/
static void pdc20230_set_piomode(struct ata_port *ap, struct ata_device *adev)
@@ -221,8 +306,7 @@ static void pdc20230_set_piomode(struct ata_port *ap, struct ata_device *adev)
local_irq_save(flags);
/* Unlock the control interface */
- do
- {
+ do {
inb(0x1F5);
outb(inb(0x1F2) | 0x80, 0x1F2);
inb(0x1F2);
@@ -231,7 +315,7 @@ static void pdc20230_set_piomode(struct ata_port *ap, struct ata_device *adev)
inb(0x1F2);
inb(0x1F2);
}
- while((inb(0x1F2) & 0x80) && --tries);
+ while ((inb(0x1F2) & 0x80) && --tries);
local_irq_restore(flags);
@@ -249,13 +333,14 @@ static void pdc20230_set_piomode(struct ata_port *ap, struct ata_device *adev)
}
-static void pdc_data_xfer_vlb(struct ata_device *adev, unsigned char *buf, unsigned int buflen, int write_data)
+static unsigned int pdc_data_xfer_vlb(struct ata_device *dev,
+ unsigned char *buf, unsigned int buflen, int rw)
{
- struct ata_port *ap = adev->link->ap;
- int slop = buflen & 3;
- unsigned long flags;
+ if (ata_id_has_dword_io(dev->id)) {
+ struct ata_port *ap = dev->link->ap;
+ int slop = buflen & 3;
+ unsigned long flags;
- if (ata_id_has_dword_io(adev->id)) {
local_irq_save(flags);
/* Perform the 32bit I/O synchronization sequence */
@@ -264,26 +349,27 @@ static void pdc_data_xfer_vlb(struct ata_device *adev, unsigned char *buf, unsig
ioread8(ap->ioaddr.nsect_addr);
/* Now the data */
-
- if (write_data)
- iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
- else
+ if (rw == READ)
ioread32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
+ else
+ iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
if (unlikely(slop)) {
- __le32 pad = 0;
- if (write_data) {
- memcpy(&pad, buf + buflen - slop, slop);
- iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr);
- } else {
+ u32 pad;
+ if (rw == READ) {
pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr));
memcpy(buf + buflen - slop, &pad, slop);
+ } else {
+ memcpy(&pad, buf + buflen - slop, slop);
+ iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr);
}
+ buflen += 4 - slop;
}
local_irq_restore(flags);
- }
- else
- ata_data_xfer_noirq(adev, buf, buflen, write_data);
+ } else
+ buflen = ata_data_xfer_noirq(dev, buf, buflen, rw);
+
+ return buflen;
}
static struct ata_port_operations pdc20230_port_ops = {
@@ -310,14 +396,14 @@ static struct ata_port_operations pdc20230_port_ops = {
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
- .port_start = ata_port_start,
+ .port_start = ata_sff_port_start,
};
/*
* Holtek 6560A support
*
- * This controller supports PIO0 to PIO2 (no IORDY even though higher timings
- * can be loaded).
+ * This controller supports PIO0 to PIO2 (no IORDY even though higher
+ * timings can be loaded).
*/
static void ht6560a_set_piomode(struct ata_port *ap, struct ata_device *adev)
@@ -364,14 +450,14 @@ static struct ata_port_operations ht6560a_port_ops = {
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
- .port_start = ata_port_start,
+ .port_start = ata_sff_port_start,
};
/*
* Holtek 6560B support
*
- * This controller supports PIO0 to PIO4. We honour the BIOS/jumper FIFO setting
- * unless we see an ATAPI device in which case we force it off.
+ * This controller supports PIO0 to PIO4. We honour the BIOS/jumper FIFO
+ * setting unless we see an ATAPI device in which case we force it off.
*
* FIXME: need to implement 2nd channel support.
*/
@@ -398,7 +484,7 @@ static void ht6560b_set_piomode(struct ata_port *ap, struct ata_device *adev)
if (adev->class != ATA_DEV_ATA) {
u8 rconf = inb(0x3E6);
if (rconf & 0x24) {
- rconf &= ~ 0x24;
+ rconf &= ~0x24;
outb(rconf, 0x3E6);
}
}
@@ -423,13 +509,13 @@ static struct ata_port_operations ht6560b_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .data_xfer = ata_data_xfer, /* FIXME: Check 32bit and noirq */
+ .data_xfer = ata_data_xfer, /* FIXME: Check 32bit and noirq */
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
- .port_start = ata_port_start,
+ .port_start = ata_sff_port_start,
};
/*
@@ -462,7 +548,8 @@ static u8 opti_syscfg(u8 reg)
* This controller supports PIO0 to PIO3.
*/
-static void opti82c611a_set_piomode(struct ata_port *ap, struct ata_device *adev)
+static void opti82c611a_set_piomode(struct ata_port *ap,
+ struct ata_device *adev)
{
u8 active, recover, setup;
struct ata_timing t;
@@ -549,7 +636,7 @@ static struct ata_port_operations opti82c611a_port_ops = {
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
- .port_start = ata_port_start,
+ .port_start = ata_sff_port_start,
};
/*
@@ -681,77 +768,398 @@ static struct ata_port_operations opti82c46x_port_ops = {
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
- .port_start = ata_port_start,
+ .port_start = ata_sff_port_start,
};
+static void qdi6500_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+ struct ata_timing t;
+ struct legacy_data *qdi = ap->host->private_data;
+ int active, recovery;
+ u8 timing;
+
+ /* Get the timing data in cycles */
+ ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);
+
+ if (qdi->fast) {
+ active = 8 - FIT(t.active, 1, 8);
+ recovery = 18 - FIT(t.recover, 3, 18);
+ } else {
+ active = 9 - FIT(t.active, 2, 9);
+ recovery = 15 - FIT(t.recover, 0, 15);
+ }
+ timing = (recovery << 4) | active | 0x08;
+
+ qdi->clock[adev->devno] = timing;
+
+ outb(timing, qdi->timing);
+}
/**
- * legacy_init_one - attach a legacy interface
- * @port: port number
- * @io: I/O port start
- * @ctrl: control port
+ * qdi6580dp_set_piomode - PIO setup for dual channel
+ * @ap: Port
+ * @adev: Device
* @irq: interrupt line
*
- * Register an ISA bus IDE interface. Such interfaces are PIO and we
- * assume do not support IRQ sharing.
+ * In dual channel mode the 6580 has one clock per channel and we have
+ * to software clockswitch in qc_issue_prot.
*/
-static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl, int irq)
+static void qdi6580dp_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
- struct legacy_data *ld = &legacy_data[nr_legacy_host];
- struct ata_host *host;
- struct ata_port *ap;
- struct platform_device *pdev;
- struct ata_port_operations *ops = &legacy_port_ops;
- void __iomem *io_addr, *ctrl_addr;
- int pio_modes = pio_mask;
- u32 mask = (1 << port);
- u32 iordy = (iordy_mask & mask) ? 0: ATA_FLAG_NO_IORDY;
- int ret;
+ struct ata_timing t;
+ struct legacy_data *qdi = ap->host->private_data;
+ int active, recovery;
+ u8 timing;
- pdev = platform_device_register_simple(DRV_NAME, nr_legacy_host, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
+ /* Get the timing data in cycles */
+ ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);
+
+ if (qdi->fast) {
+ active = 8 - FIT(t.active, 1, 8);
+ recovery = 18 - FIT(t.recover, 3, 18);
+ } else {
+ active = 9 - FIT(t.active, 2, 9);
+ recovery = 15 - FIT(t.recover, 0, 15);
+ }
+ timing = (recovery << 4) | active | 0x08;
- ret = -EBUSY;
- if (devm_request_region(&pdev->dev, io, 8, "pata_legacy") == NULL ||
- devm_request_region(&pdev->dev, ctrl, 1, "pata_legacy") == NULL)
- goto fail;
+ qdi->clock[adev->devno] = timing;
- ret = -ENOMEM;
- io_addr = devm_ioport_map(&pdev->dev, io, 8);
- ctrl_addr = devm_ioport_map(&pdev->dev, ctrl, 1);
- if (!io_addr || !ctrl_addr)
- goto fail;
+ outb(timing, qdi->timing + 2 * ap->port_no);
+ /* Clear the FIFO */
+ if (adev->class != ATA_DEV_ATA)
+ outb(0x5F, qdi->timing + 3);
+}
- if (ht6560a & mask) {
- ops = &ht6560a_port_ops;
- pio_modes = 0x07;
- iordy = ATA_FLAG_NO_IORDY;
- }
- if (ht6560b & mask) {
- ops = &ht6560b_port_ops;
- pio_modes = 0x1F;
- }
- if (opti82c611a & mask) {
- ops = &opti82c611a_port_ops;
- pio_modes = 0x0F;
+/**
+ * qdi6580_set_piomode - PIO setup for single channel
+ * @ap: Port
+ * @adev: Device
+ *
+ * In single channel mode the 6580 has one clock per device and we can
+ * avoid the requirement to clock switch. We also have to load the timing
+ * into the right clock according to whether we are master or slave.
+ */
+
+static void qdi6580_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+ struct ata_timing t;
+ struct legacy_data *qdi = ap->host->private_data;
+ int active, recovery;
+ u8 timing;
+
+ /* Get the timing data in cycles */
+ ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);
+
+ if (qdi->fast) {
+ active = 8 - FIT(t.active, 1, 8);
+ recovery = 18 - FIT(t.recover, 3, 18);
+ } else {
+ active = 9 - FIT(t.active, 2, 9);
+ recovery = 15 - FIT(t.recover, 0, 15);
}
- if (opti82c46x & mask) {
- ops = &opti82c46x_port_ops;
- pio_modes = 0x0F;
+ timing = (recovery << 4) | active | 0x08;
+ qdi->clock[adev->devno] = timing;
+ outb(timing, qdi->timing + 2 * adev->devno);
+ /* Clear the FIFO */
+ if (adev->class != ATA_DEV_ATA)
+ outb(0x5F, qdi->timing + 3);
+}
+
+/**
+ * qdi_qc_issue_prot - command issue
+ * @qc: command pending
+ *
+ * Called when the libata layer is about to issue a command. We wrap
+ * this interface so that we can load the correct ATA timings.
+ */
+
+static unsigned int qdi_qc_issue_prot(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ata_device *adev = qc->dev;
+ struct legacy_data *qdi = ap->host->private_data;
+
+ if (qdi->clock[adev->devno] != qdi->last) {
+ if (adev->pio_mode) {
+ qdi->last = qdi->clock[adev->devno];
+ outb(qdi->clock[adev->devno], qdi->timing +
+ 2 * ap->port_no);
+ }
}
+ return ata_qc_issue_prot(qc);
+}
- /* Probe for automatically detectable controllers */
+static unsigned int vlb32_data_xfer(struct ata_device *adev, unsigned char *buf,
+ unsigned int buflen, int rw)
+{
+ struct ata_port *ap = adev->link->ap;
+ int slop = buflen & 3;
- if (io == 0x1F0 && ops == &legacy_port_ops) {
- unsigned long flags;
+ if (ata_id_has_dword_io(adev->id)) {
+ if (rw == WRITE)
+ iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
+ else
+ ioread32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
- local_irq_save(flags);
+ if (unlikely(slop)) {
+ u32 pad;
+ if (rw == WRITE) {
+ memcpy(&pad, buf + buflen - slop, slop);
+ pad = le32_to_cpu(pad);
+ iowrite32(pad, ap->ioaddr.data_addr);
+ } else {
+ pad = ioread32(ap->ioaddr.data_addr);
+ pad = cpu_to_le32(pad);
+ memcpy(buf + buflen - slop, &pad, slop);
+ }
+ }
+ return (buflen + 3) & ~3;
+ } else
+ return ata_data_xfer(adev, buf, buflen, rw);
+}
+
+static int qdi_port(struct platform_device *dev,
+ struct legacy_probe *lp, struct legacy_data *ld)
+{
+ if (devm_request_region(&dev->dev, lp->private, 4, "qdi") == NULL)
+ return -EBUSY;
+ ld->timing = lp->private;
+ return 0;
+}
+
+static struct ata_port_operations qdi6500_port_ops = {
+ .set_piomode = qdi6500_set_piomode,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .freeze = ata_bmdma_freeze,
+ .thaw = ata_bmdma_thaw,
+ .error_handler = ata_bmdma_error_handler,
+ .post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = qdi_qc_issue_prot,
+
+ .data_xfer = vlb32_data_xfer,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .irq_on = ata_irq_on,
+
+ .port_start = ata_sff_port_start,
+};
+
+static struct ata_port_operations qdi6580_port_ops = {
+ .set_piomode = qdi6580_set_piomode,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .freeze = ata_bmdma_freeze,
+ .thaw = ata_bmdma_thaw,
+ .error_handler = ata_bmdma_error_handler,
+ .post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .data_xfer = vlb32_data_xfer,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .irq_on = ata_irq_on,
+
+ .port_start = ata_sff_port_start,
+};
+
+static struct ata_port_operations qdi6580dp_port_ops = {
+ .set_piomode = qdi6580dp_set_piomode,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .freeze = ata_bmdma_freeze,
+ .thaw = ata_bmdma_thaw,
+ .error_handler = ata_bmdma_error_handler,
+ .post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = qdi_qc_issue_prot,
+
+ .data_xfer = vlb32_data_xfer,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .irq_on = ata_irq_on,
+
+ .port_start = ata_sff_port_start,
+};
+
+static DEFINE_SPINLOCK(winbond_lock);
+
+static void winbond_writecfg(unsigned long port, u8 reg, u8 val)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&winbond_lock, flags);
+ outb(reg, port + 0x01);
+ outb(val, port + 0x02);
+ spin_unlock_irqrestore(&winbond_lock, flags);
+}
+
+static u8 winbond_readcfg(unsigned long port, u8 reg)
+{
+ u8 val;
+
+ unsigned long flags;
+ spin_lock_irqsave(&winbond_lock, flags);
+ outb(reg, port + 0x01);
+ val = inb(port + 0x02);
+ spin_unlock_irqrestore(&winbond_lock, flags);
+
+ return val;
+}
+
+static void winbond_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+ struct ata_timing t;
+ struct legacy_data *winbond = ap->host->private_data;
+ int active, recovery;
+ u8 reg;
+ int timing = 0x88 + (ap->port_no * 4) + (adev->devno * 2);
+
+ reg = winbond_readcfg(winbond->timing, 0x81);
+
+ /* Get the timing data in cycles */
+ if (reg & 0x40) /* Fast VLB bus, assume 50MHz */
+ ata_timing_compute(adev, adev->pio_mode, &t, 20000, 1000);
+ else
+ ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);
+
+ active = (FIT(t.active, 3, 17) - 1) & 0x0F;
+ recovery = (FIT(t.recover, 1, 15) + 1) & 0x0F;
+ timing = (active << 4) | recovery;
+ winbond_writecfg(winbond->timing, timing, reg);
+
+ /* Load the setup timing */
+
+ reg = 0x35;
+ if (adev->class != ATA_DEV_ATA)
+ reg |= 0x08; /* FIFO off */
+ if (!ata_pio_need_iordy(adev))
+ reg |= 0x02; /* IORDY off */
+ reg |= (FIT(t.setup, 0, 3) << 6);
+ winbond_writecfg(winbond->timing, timing + 1, reg);
+}
+
+static int winbond_port(struct platform_device *dev,
+ struct legacy_probe *lp, struct legacy_data *ld)
+{
+ if (devm_request_region(&dev->dev, lp->private, 4, "winbond") == NULL)
+ return -EBUSY;
+ ld->timing = lp->private;
+ return 0;
+}
+
+static struct ata_port_operations winbond_port_ops = {
+ .set_piomode = winbond_set_piomode,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .freeze = ata_bmdma_freeze,
+ .thaw = ata_bmdma_thaw,
+ .error_handler = ata_bmdma_error_handler,
+ .post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .data_xfer = vlb32_data_xfer,
+
+ .irq_clear = ata_bmdma_irq_clear,
+ .irq_on = ata_irq_on,
+ .port_start = ata_sff_port_start,
+};
+
+static struct legacy_controller controllers[] = {
+ {"BIOS", &legacy_port_ops, 0x1F,
+ ATA_FLAG_NO_IORDY, NULL },
+ {"Snooping", &simple_port_ops, 0x1F,
+ 0 , NULL },
+ {"PDC20230", &pdc20230_port_ops, 0x7,
+ ATA_FLAG_NO_IORDY, NULL },
+ {"HT6560A", &ht6560a_port_ops, 0x07,
+ ATA_FLAG_NO_IORDY, NULL },
+ {"HT6560B", &ht6560b_port_ops, 0x1F,
+ ATA_FLAG_NO_IORDY, NULL },
+ {"OPTI82C611A", &opti82c611a_port_ops, 0x0F,
+ 0 , NULL },
+ {"OPTI82C46X", &opti82c46x_port_ops, 0x0F,
+ 0 , NULL },
+ {"QDI6500", &qdi6500_port_ops, 0x07,
+ ATA_FLAG_NO_IORDY, qdi_port },
+ {"QDI6580", &qdi6580_port_ops, 0x1F,
+ 0 , qdi_port },
+ {"QDI6580DP", &qdi6580dp_port_ops, 0x1F,
+ 0 , qdi_port },
+ {"W83759A", &winbond_port_ops, 0x1F,
+ 0 , winbond_port }
+};
+
+/**
+ * probe_chip_type - Discover controller
+ * @probe: Probe entry to check
+ *
+ * Probe an ATA port and identify the type of controller. We don't
+ * check if the controller appears to be driveless at this point.
+ */
+
+static __init int probe_chip_type(struct legacy_probe *probe)
+{
+ int mask = 1 << probe->slot;
+
+ if (winbond && (probe->port == 0x1F0 || probe->port == 0x170)) {
+ u8 reg = winbond_readcfg(winbond, 0x81);
+ reg |= 0x80; /* jumpered mode off */
+ winbond_writecfg(winbond, 0x81, reg);
+ reg = winbond_readcfg(winbond, 0x83);
+ reg |= 0xF0; /* local control */
+ winbond_writecfg(winbond, 0x83, reg);
+ reg = winbond_readcfg(winbond, 0x85);
+ reg |= 0xF0; /* programmable timing */
+ winbond_writecfg(winbond, 0x85, reg);
+
+ reg = winbond_readcfg(winbond, 0x81);
+
+ if (reg & mask)
+ return W83759A;
+ }
+ if (probe->port == 0x1F0) {
+ unsigned long flags;
+ local_irq_save(flags);
/* Probes */
- inb(0x1F5);
outb(inb(0x1F2) | 0x80, 0x1F2);
+ inb(0x1F5);
inb(0x1F2);
inb(0x3F6);
inb(0x3F6);
@@ -760,29 +1168,83 @@ static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl
if ((inb(0x1F2) & 0x80) == 0) {
/* PDC20230c or 20630 ? */
- printk(KERN_INFO "PDC20230-C/20630 VLB ATA controller detected.\n");
- pio_modes = 0x07;
- ops = &pdc20230_port_ops;
- iordy = ATA_FLAG_NO_IORDY;
+ printk(KERN_INFO "PDC20230-C/20630 VLB ATA controller"
+ " detected.\n");
udelay(100);
inb(0x1F5);
+ local_irq_restore(flags);
+ return PDC20230;
} else {
outb(0x55, 0x1F2);
inb(0x1F2);
inb(0x1F2);
- if (inb(0x1F2) == 0x00) {
- printk(KERN_INFO "PDC20230-B VLB ATA controller detected.\n");
- }
+ if (inb(0x1F2) == 0x00)
+ printk(KERN_INFO "PDC20230-B VLB ATA "
+ "controller detected.\n");
+ local_irq_restore(flags);
+ return BIOS;
}
local_irq_restore(flags);
}
+ if (ht6560a & mask)
+ return HT6560A;
+ if (ht6560b & mask)
+ return HT6560B;
+ if (opti82c611a & mask)
+ return OPTI611A;
+ if (opti82c46x & mask)
+ return OPTI46X;
+ if (autospeed & mask)
+ return SNOOP;
+ return BIOS;
+}
+
+
+/**
+ * legacy_init_one - attach a legacy interface
+ * @pl: probe record
+ *
+ * Register an ISA bus IDE interface. Such interfaces are PIO and we
+ * assume do not support IRQ sharing.
+ */
+
+static __init int legacy_init_one(struct legacy_probe *probe)
+{
+ struct legacy_controller *controller = &controllers[probe->type];
+ int pio_modes = controller->pio_mask;
+ unsigned long io = probe->port;
+ u32 mask = (1 << probe->slot);
+ struct ata_port_operations *ops = controller->ops;
+ struct legacy_data *ld = &legacy_data[probe->slot];
+ struct ata_host *host = NULL;
+ struct ata_port *ap;
+ struct platform_device *pdev;
+ struct ata_device *dev;
+ void __iomem *io_addr, *ctrl_addr;
+ u32 iordy = (iordy_mask & mask) ? 0: ATA_FLAG_NO_IORDY;
+ int ret;
- /* Chip does mode setting by command snooping */
- if (ops == &legacy_port_ops && (autospeed & mask))
- ops = &simple_port_ops;
+ iordy |= controller->flags;
+
+ pdev = platform_device_register_simple(DRV_NAME, probe->slot, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ ret = -EBUSY;
+ if (devm_request_region(&pdev->dev, io, 8, "pata_legacy") == NULL ||
+ devm_request_region(&pdev->dev, io + 0x0206, 1,
+ "pata_legacy") == NULL)
+ goto fail;
ret = -ENOMEM;
+ io_addr = devm_ioport_map(&pdev->dev, io, 8);
+ ctrl_addr = devm_ioport_map(&pdev->dev, io + 0x0206, 1);
+ if (!io_addr || !ctrl_addr)
+ goto fail;
+ if (controller->setup)
+ if (controller->setup(pdev, probe, ld) < 0)
+ goto fail;
host = ata_host_alloc(&pdev->dev, 1);
if (!host)
goto fail;
@@ -795,19 +1257,29 @@ static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl
ap->ioaddr.altstatus_addr = ctrl_addr;
ap->ioaddr.ctl_addr = ctrl_addr;
ata_std_ports(&ap->ioaddr);
- ap->private_data = ld;
+ ap->host->private_data = ld;
- ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", io, ctrl);
+ ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", io, io + 0x0206);
- ret = ata_host_activate(host, irq, ata_interrupt, 0, &legacy_sht);
+ ret = ata_host_activate(host, probe->irq, ata_interrupt, 0,
+ &legacy_sht);
if (ret)
goto fail;
-
- legacy_host[nr_legacy_host++] = dev_get_drvdata(&pdev->dev);
ld->platform_dev = pdev;
- return 0;
+ /* Nothing found means we drop the port as its probably not there */
+
+ ret = -ENODEV;
+ ata_link_for_each_dev(dev, &ap->link) {
+ if (!ata_dev_absent(dev)) {
+ legacy_host[probe->slot] = host;
+ ld->platform_dev = pdev;
+ return 0;
+ }
+ }
fail:
+ if (host)
+ ata_host_detach(host);
platform_device_unregister(pdev);
return ret;
}
@@ -818,13 +1290,15 @@ static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl
* @master: set this if we find an ATA master
* @master: set this if we find an ATA secondary
*
- * A small number of vendors implemented early PCI ATA interfaces on bridge logic
- * without the ATA interface being PCI visible. Where we have a matching PCI driver
- * we must skip the relevant device here. If we don't know about it then the legacy
- * driver is the right driver anyway.
+ * A small number of vendors implemented early PCI ATA interfaces
+ * on bridge logic without the ATA interface being PCI visible.
+ * Where we have a matching PCI driver we must skip the relevant
+ * device here. If we don't know about it then the legacy driver
+ * is the right driver anyway.
*/
-static void legacy_check_special_cases(struct pci_dev *p, int *primary, int *secondary)
+static void __init legacy_check_special_cases(struct pci_dev *p, int *primary,
+ int *secondary)
{
/* Cyrix CS5510 pre SFF MWDMA ATA on the bridge */
if (p->vendor == 0x1078 && p->device == 0x0000) {
@@ -840,7 +1314,8 @@ static void legacy_check_special_cases(struct pci_dev *p, int *primary, int *sec
if (p->vendor == 0x8086 && p->device == 0x1234) {
u16 r;
pci_read_config_word(p, 0x6C, &r);
- if (r & 0x8000) { /* ATA port enabled */
+ if (r & 0x8000) {
+ /* ATA port enabled */
if (r & 0x4000)
*secondary = 1;
else
@@ -850,6 +1325,114 @@ static void legacy_check_special_cases(struct pci_dev *p, int *primary, int *sec
}
}
+static __init void probe_opti_vlb(void)
+{
+ /* If an OPTI 82C46X is present find out where the channels are */
+ static const char *optis[4] = {
+ "3/463MV", "5MV",
+ "5MVA", "5MVB"
+ };
+ u8 chans = 1;
+ u8 ctrl = (opti_syscfg(0x30) & 0xC0) >> 6;
+
+ opti82c46x = 3; /* Assume master and slave first */
+ printk(KERN_INFO DRV_NAME ": Opti 82C46%s chipset support.\n",
+ optis[ctrl]);
+ if (ctrl == 3)
+ chans = (opti_syscfg(0x3F) & 0x20) ? 2 : 1;
+ ctrl = opti_syscfg(0xAC);
+ /* Check enabled and this port is the 465MV port. On the
+ MVB we may have two channels */
+ if (ctrl & 8) {
+ if (chans == 2) {
+ legacy_probe_add(0x1F0, 14, OPTI46X, 0);
+ legacy_probe_add(0x170, 15, OPTI46X, 0);
+ }
+ if (ctrl & 4)
+ legacy_probe_add(0x170, 15, OPTI46X, 0);
+ else
+ legacy_probe_add(0x1F0, 14, OPTI46X, 0);
+ } else
+ legacy_probe_add(0x1F0, 14, OPTI46X, 0);
+}
+
+static __init void qdi65_identify_port(u8 r, u8 res, unsigned long port)
+{
+ static const unsigned long ide_port[2] = { 0x170, 0x1F0 };
+ /* Check card type */
+ if ((r & 0xF0) == 0xC0) {
+ /* QD6500: single channel */
+ if (r & 8)
+ /* Disabled ? */
+ return;
+ legacy_probe_add(ide_port[r & 0x01], 14 + (r & 0x01),
+ QDI6500, port);
+ }
+ if (((r & 0xF0) == 0xA0) || (r & 0xF0) == 0x50) {
+ /* QD6580: dual channel */
+ if (!request_region(port + 2 , 2, "pata_qdi")) {
+ release_region(port, 2);
+ return;
+ }
+ res = inb(port + 3);
+ /* Single channel mode ? */
+ if (res & 1)
+ legacy_probe_add(ide_port[r & 0x01], 14 + (r & 0x01),
+ QDI6580, port);
+ else { /* Dual channel mode */
+ legacy_probe_add(0x1F0, 14, QDI6580DP, port);
+ /* port + 0x02, r & 0x04 */
+ legacy_probe_add(0x170, 15, QDI6580DP, port + 2);
+ }
+ release_region(port + 2, 2);
+ }
+}
+
+static __init void probe_qdi_vlb(void)
+{
+ unsigned long flags;
+ static const unsigned long qd_port[2] = { 0x30, 0xB0 };
+ int i;
+
+ /*
+ * Check each possible QD65xx base address
+ */
+
+ for (i = 0; i < 2; i++) {
+ unsigned long port = qd_port[i];
+ u8 r, res;
+
+
+ if (request_region(port, 2, "pata_qdi")) {
+ /* Check for a card */
+ local_irq_save(flags);
+ /* I have no h/w that needs this delay but it
+ is present in the historic code */
+ r = inb(port);
+ udelay(1);
+ outb(0x19, port);
+ udelay(1);
+ res = inb(port);
+ udelay(1);
+ outb(r, port);
+ udelay(1);
+ local_irq_restore(flags);
+
+ /* Fail */
+ if (res == 0x19) {
+ release_region(port, 2);
+ continue;
+ }
+ /* Passes the presence test */
+ r = inb(port + 1);
+ udelay(1);
+ /* Check port agrees with port set */
+ if ((r & 2) >> 1 == i)
+ qdi65_identify_port(r, res, port);
+ release_region(port, 2);
+ }
+ }
+}
/**
* legacy_init - attach legacy interfaces
@@ -867,15 +1450,17 @@ static __init int legacy_init(void)
int ct = 0;
int primary = 0;
int secondary = 0;
- int last_port = NR_HOST;
+ int pci_present = 0;
+ struct legacy_probe *pl = &probe_list[0];
+ int slot = 0;
struct pci_dev *p = NULL;
for_each_pci_dev(p) {
int r;
- /* Check for any overlap of the system ATA mappings. Native mode controllers
- stuck on these addresses or some devices in 'raid' mode won't be found by
- the storage class test */
+ /* Check for any overlap of the system ATA mappings. Native
+ mode controllers stuck on these addresses or some devices
+ in 'raid' mode won't be found by the storage class test */
for (r = 0; r < 6; r++) {
if (pci_resource_start(p, r) == 0x1f0)
primary = 1;
@@ -885,49 +1470,39 @@ static __init int legacy_init(void)
/* Check for special cases */
legacy_check_special_cases(p, &primary, &secondary);
- /* If PCI bus is present then don't probe for tertiary legacy ports */
- if (probe_all == 0)
- last_port = 2;
+ /* If PCI bus is present then don't probe for tertiary
+ legacy ports */
+ pci_present = 1;
}
- /* If an OPTI 82C46X is present find out where the channels are */
- if (opti82c46x) {
- static const char *optis[4] = {
- "3/463MV", "5MV",
- "5MVA", "5MVB"
- };
- u8 chans = 1;
- u8 ctrl = (opti_syscfg(0x30) & 0xC0) >> 6;
-
- opti82c46x = 3; /* Assume master and slave first */
- printk(KERN_INFO DRV_NAME ": Opti 82C46%s chipset support.\n", optis[ctrl]);
- if (ctrl == 3)
- chans = (opti_syscfg(0x3F) & 0x20) ? 2 : 1;
- ctrl = opti_syscfg(0xAC);
- /* Check enabled and this port is the 465MV port. On the
- MVB we may have two channels */
- if (ctrl & 8) {
- if (ctrl & 4)
- opti82c46x = 2; /* Slave */
- else
- opti82c46x = 1; /* Master */
- if (chans == 2)
- opti82c46x = 3; /* Master and Slave */
- } /* Slave only */
- else if (chans == 1)
- opti82c46x = 1;
+ if (winbond == 1)
+ winbond = 0x130; /* Default port, alt is 1B0 */
+
+ if (primary == 0 || all)
+ legacy_probe_add(0x1F0, 14, UNKNOWN, 0);
+ if (secondary == 0 || all)
+ legacy_probe_add(0x170, 15, UNKNOWN, 0);
+
+ if (probe_all || !pci_present) {
+ /* ISA/VLB extra ports */
+ legacy_probe_add(0x1E8, 11, UNKNOWN, 0);
+ legacy_probe_add(0x168, 10, UNKNOWN, 0);
+ legacy_probe_add(0x1E0, 8, UNKNOWN, 0);
+ legacy_probe_add(0x160, 12, UNKNOWN, 0);
}
- for (i = 0; i < last_port; i++) {
- /* Skip primary if we have seen a PCI one */
- if (i == 0 && primary == 1)
- continue;
- /* Skip secondary if we have seen a PCI one */
- if (i == 1 && secondary == 1)
+ if (opti82c46x)
+ probe_opti_vlb();
+ if (qdi)
+ probe_qdi_vlb();
+
+ for (i = 0; i < NR_HOST; i++, pl++) {
+ if (pl->port == 0)
continue;
- if (legacy_init_one(i, legacy_port[i],
- legacy_port[i] + 0x0206,
- legacy_irq[i]) == 0)
+ if (pl->type == UNKNOWN)
+ pl->type = probe_chip_type(pl);
+ pl->slot = slot++;
+ if (legacy_init_one(pl) == 0)
ct++;
}
if (ct != 0)
@@ -941,11 +1516,8 @@ static __exit void legacy_exit(void)
for (i = 0; i < nr_legacy_host; i++) {
struct legacy_data *ld = &legacy_data[i];
-
ata_host_detach(legacy_host[i]);
platform_device_unregister(ld->platform_dev);
- if (ld->timing)
- release_region(ld->timing, 2);
}
}
@@ -960,9 +1532,9 @@ module_param(ht6560a, int, 0);
module_param(ht6560b, int, 0);
module_param(opti82c611a, int, 0);
module_param(opti82c46x, int, 0);
+module_param(qdi, int, 0);
module_param(pio_mask, int, 0);
module_param(iordy_mask, int, 0);
module_init(legacy_init);
module_exit(legacy_exit);
-
diff --git a/trunk/drivers/ata/pata_mpc52xx.c b/trunk/drivers/ata/pata_mpc52xx.c
index 50c56e2814c1..dc401626cdb2 100644
--- a/trunk/drivers/ata/pata_mpc52xx.c
+++ b/trunk/drivers/ata/pata_mpc52xx.c
@@ -364,7 +364,7 @@ mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match)
{
unsigned int ipb_freq;
struct resource res_mem;
- int ata_irq = NO_IRQ;
+ int ata_irq;
struct mpc52xx_ata __iomem *ata_regs;
struct mpc52xx_ata_priv *priv;
int rv;
diff --git a/trunk/drivers/ata/pata_ninja32.c b/trunk/drivers/ata/pata_ninja32.c
new file mode 100644
index 000000000000..1c1b83541d13
--- /dev/null
+++ b/trunk/drivers/ata/pata_ninja32.c
@@ -0,0 +1,214 @@
+/*
+ * pata_ninja32.c - Ninja32 PATA for new ATA layer
+ * (C) 2007 Red Hat Inc
+ * Alan Cox
+ *
+ * Note: The controller like many controllers has shared timings for
+ * PIO and DMA. We thus flip to the DMA timings in dma_start and flip back
+ * in the dma_stop function. Thus we actually don't need a set_dmamode
+ * method as the PIO method is always called and will set the right PIO
+ * timing parameters.
+ *
+ * The Ninja32 Cardbus is not a generic SFF controller. Instead it is
+ * laid out as follows off BAR 0. This is based upon Mark Lord's delkin
+ * driver and the extensive analysis done by the BSD developers, notably
+ * ITOH Yasufumi.
+ *
+ * Base + 0x00 IRQ Status
+ * Base + 0x01 IRQ control
+ * Base + 0x02 Chipset control
+ * Base + 0x04 VDMA and reset control + wait bits
+ * Base + 0x08 BMIMBA
+ * Base + 0x0C DMA Length
+ * Base + 0x10 Taskfile
+ * Base + 0x18 BMDMA Status ?
+ * Base + 0x1C
+ * Base + 0x1D Bus master control
+ * bit 0 = enable
+ * bit 1 = 0 write/1 read
+ * bit 2 = 1 sgtable
+ * bit 3 = go
+ * bit 4-6 wait bits
+ * bit 7 = done
+ * Base + 0x1E AltStatus
+ * Base + 0x1F timing register
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define DRV_NAME "pata_ninja32"
+#define DRV_VERSION "0.0.1"
+
+
+/**
+ * ninja32_set_piomode - set initial PIO mode data
+ * @ap: ATA interface
+ * @adev: ATA device
+ *
+ * Called to do the PIO mode setup. Our timing registers are shared
+ * but we want to set the PIO timing by default.
+ */
+
+static void ninja32_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+ static u16 pio_timing[5] = {
+ 0xd6, 0x85, 0x44, 0x33, 0x13
+ };
+ iowrite8(pio_timing[adev->pio_mode - XFER_PIO_0],
+ ap->ioaddr.bmdma_addr + 0x1f);
+ ap->private_data = adev;
+}
+
+
+static void ninja32_dev_select(struct ata_port *ap, unsigned int device)
+{
+ struct ata_device *adev = &ap->link.device[device];
+ if (ap->private_data != adev) {
+ iowrite8(0xd6, ap->ioaddr.bmdma_addr + 0x1f);
+ ata_std_dev_select(ap, device);
+ ninja32_set_piomode(ap, adev);
+ }
+}
+
+static struct scsi_host_template ninja32_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .slave_destroy = ata_scsi_slave_destroy,
+ .bios_param = ata_std_bios_param,
+};
+
+static struct ata_port_operations ninja32_port_ops = {
+ .set_piomode = ninja32_set_piomode,
+ .mode_filter = ata_pci_default_filter,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ninja32_dev_select,
+
+ .freeze = ata_bmdma_freeze,
+ .thaw = ata_bmdma_thaw,
+ .error_handler = ata_bmdma_error_handler,
+ .post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
+
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .data_xfer = ata_data_xfer,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .irq_on = ata_irq_on,
+
+ .port_start = ata_sff_port_start,
+};
+
+static int ninja32_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct ata_host *host;
+ struct ata_port *ap;
+ void __iomem *base;
+ int rc;
+
+ host = ata_host_alloc(&dev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+ ap = host->ports[0];
+
+ /* Set up the PCI device */
+ rc = pcim_enable_device(dev);
+ if (rc)
+ return rc;
+ rc = pcim_iomap_regions(dev, 1 << 0, DRV_NAME);
+ if (rc == -EBUSY)
+ pcim_pin_device(dev);
+ if (rc)
+ return rc;
+
+ host->iomap = pcim_iomap_table(dev);
+ rc = pci_set_dma_mask(dev, ATA_DMA_MASK);
+ if (rc)
+ return rc;
+ rc = pci_set_consistent_dma_mask(dev, ATA_DMA_MASK);
+ if (rc)
+ return rc;
+ pci_set_master(dev);
+
+ /* Set up the register mappings */
+ base = host->iomap[0];
+ if (!base)
+ return -ENOMEM;
+ ap->ops = &ninja32_port_ops;
+ ap->pio_mask = 0x1F;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+
+ ap->ioaddr.cmd_addr = base + 0x10;
+ ap->ioaddr.ctl_addr = base + 0x1E;
+ ap->ioaddr.altstatus_addr = base + 0x1E;
+ ap->ioaddr.bmdma_addr = base;
+ ata_std_ports(&ap->ioaddr);
+
+ iowrite8(0x05, base + 0x01); /* Enable interrupt lines */
+ iowrite8(0xB3, base + 0x02); /* Burst, ?? setup */
+ iowrite8(0x00, base + 0x04); /* WAIT0 ? */
+ /* FIXME: Should we disable them at remove ? */
+ return ata_host_activate(host, dev->irq, ata_interrupt,
+ IRQF_SHARED, &ninja32_sht);
+}
+
+static const struct pci_device_id ninja32[] = {
+ { 0x1145, 0xf021, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1145, 0xf024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { },
+};
+
+static struct pci_driver ninja32_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ninja32,
+ .probe = ninja32_init_one,
+ .remove = ata_pci_remove_one
+};
+
+static int __init ninja32_init(void)
+{
+ return pci_register_driver(&ninja32_pci_driver);
+}
+
+static void __exit ninja32_exit(void)
+{
+ pci_unregister_driver(&ninja32_pci_driver);
+}
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("low-level driver for Ninja32 ATA");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, ninja32);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(ninja32_init);
+module_exit(ninja32_exit);
diff --git a/trunk/drivers/ata/pata_pcmcia.c b/trunk/drivers/ata/pata_pcmcia.c
index fd36099428a4..3e7f6a9da28b 100644
--- a/trunk/drivers/ata/pata_pcmcia.c
+++ b/trunk/drivers/ata/pata_pcmcia.c
@@ -42,7 +42,7 @@
#define DRV_NAME "pata_pcmcia"
-#define DRV_VERSION "0.3.2"
+#define DRV_VERSION "0.3.3"
/*
* Private data structure to glue stuff together
@@ -86,6 +86,47 @@ static int pcmcia_set_mode(struct ata_link *link, struct ata_device **r_failed_d
return ata_do_set_mode(link, r_failed_dev);
}
+/**
+ * pcmcia_set_mode_8bit - PCMCIA specific mode setup
+ * @link: link
+ * @r_failed_dev: Return pointer for failed device
+ *
+ * For the simple emulated 8bit stuff the less we do the better.
+ */
+
+static int pcmcia_set_mode_8bit(struct ata_link *link,
+ struct ata_device **r_failed_dev)
+{
+ return 0;
+}
+
+/**
+ * ata_data_xfer_8bit - Transfer data by 8bit PIO
+ * @dev: device to target
+ * @buf: data buffer
+ * @buflen: buffer length
+ * @rw: read/write
+ *
+ * Transfer data from/to the device data register by 8 bit PIO.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+
+static unsigned int ata_data_xfer_8bit(struct ata_device *dev,
+ unsigned char *buf, unsigned int buflen, int rw)
+{
+ struct ata_port *ap = dev->link->ap;
+
+ if (rw == READ)
+ ioread8_rep(ap->ioaddr.data_addr, buf, buflen);
+ else
+ iowrite8_rep(ap->ioaddr.data_addr, buf, buflen);
+
+ return buflen;
+}
+
+
static struct scsi_host_template pcmcia_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
@@ -129,6 +170,31 @@ static struct ata_port_operations pcmcia_port_ops = {
.port_start = ata_sff_port_start,
};
+static struct ata_port_operations pcmcia_8bit_port_ops = {
+ .set_mode = pcmcia_set_mode_8bit,
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .freeze = ata_bmdma_freeze,
+ .thaw = ata_bmdma_thaw,
+ .error_handler = ata_bmdma_error_handler,
+ .post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .data_xfer = ata_data_xfer_8bit,
+
+ .irq_clear = ata_bmdma_irq_clear,
+ .irq_on = ata_irq_on,
+
+ .port_start = ata_sff_port_start,
+};
+
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
@@ -153,9 +219,12 @@ static int pcmcia_init_one(struct pcmcia_device *pdev)
cistpl_cftable_entry_t dflt;
} *stk = NULL;
cistpl_cftable_entry_t *cfg;
- int pass, last_ret = 0, last_fn = 0, is_kme = 0, ret = -ENOMEM;
+ int pass, last_ret = 0, last_fn = 0, is_kme = 0, ret = -ENOMEM, p;
unsigned long io_base, ctl_base;
void __iomem *io_addr, *ctl_addr;
+ int n_ports = 1;
+
+ struct ata_port_operations *ops = &pcmcia_port_ops;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL)
@@ -282,27 +351,32 @@ static int pcmcia_init_one(struct pcmcia_device *pdev)
/* FIXME: Could be more ports at base + 0x10 but we only deal with
one right now */
if (pdev->io.NumPorts1 >= 0x20)
- printk(KERN_WARNING DRV_NAME ": second channel not yet supported.\n");
+ n_ports = 2;
+ if (pdev->manf_id == 0x0097 && pdev->card_id == 0x1620)
+ ops = &pcmcia_8bit_port_ops;
/*
* Having done the PCMCIA plumbing the ATA side is relatively
* sane.
*/
ret = -ENOMEM;
- host = ata_host_alloc(&pdev->dev, 1);
+ host = ata_host_alloc(&pdev->dev, n_ports);
if (!host)
goto failed;
- ap = host->ports[0];
- ap->ops = &pcmcia_port_ops;
- ap->pio_mask = 1; /* ISA so PIO 0 cycles */
- ap->flags |= ATA_FLAG_SLAVE_POSS;
- ap->ioaddr.cmd_addr = io_addr;
- ap->ioaddr.altstatus_addr = ctl_addr;
- ap->ioaddr.ctl_addr = ctl_addr;
- ata_std_ports(&ap->ioaddr);
+ for (p = 0; p < n_ports; p++) {
+ ap = host->ports[p];
- ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", io_base, ctl_base);
+ ap->ops = ops;
+ ap->pio_mask = 1; /* ISA so PIO 0 cycles */
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+ ap->ioaddr.cmd_addr = io_addr + 0x10 * p;
+ ap->ioaddr.altstatus_addr = ctl_addr + 0x10 * p;
+ ap->ioaddr.ctl_addr = ctl_addr + 0x10 * p;
+ ata_std_ports(&ap->ioaddr);
+
+ ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", io_base, ctl_base);
+ }
/* activate */
ret = ata_host_activate(host, pdev->irq.AssignedIRQ, ata_interrupt,
@@ -360,6 +434,7 @@ static struct pcmcia_device_id pcmcia_devices[] = {
PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704),
PCMCIA_DEVICE_MANF_CARD(0x0032, 0x2904),
PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), /* SanDisk CFA */
+ PCMCIA_DEVICE_MANF_CARD(0x0097, 0x1620), /* TI emulated */
PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000), /* Toshiba */
PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d),
PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000), /* Samsung */
diff --git a/trunk/drivers/ata/pata_pdc2027x.c b/trunk/drivers/ata/pata_pdc2027x.c
index 2622577521a1..028af5dbeed6 100644
--- a/trunk/drivers/ata/pata_pdc2027x.c
+++ b/trunk/drivers/ata/pata_pdc2027x.c
@@ -348,7 +348,7 @@ static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long
ata_id_c_string(pair->id, model_num, ATA_ID_PROD,
ATA_ID_PROD_LEN + 1);
/* If the master is a maxtor in UDMA6 then the slave should not use UDMA 6 */
- if (strstr(model_num, "Maxtor") == 0 && pair->dma_mode == XFER_UDMA_6)
+ if (strstr(model_num, "Maxtor") == NULL && pair->dma_mode == XFER_UDMA_6)
mask &= ~ (1 << (6 + ATA_SHIFT_UDMA));
return ata_pci_default_filter(adev, mask);
diff --git a/trunk/drivers/ata/pata_pdc202xx_old.c b/trunk/drivers/ata/pata_pdc202xx_old.c
index 6c9689b59b06..3ed866723e0c 100644
--- a/trunk/drivers/ata/pata_pdc202xx_old.c
+++ b/trunk/drivers/ata/pata_pdc202xx_old.c
@@ -168,8 +168,7 @@ static void pdc2026x_bmdma_start(struct ata_queued_cmd *qc)
pdc202xx_set_dmamode(ap, qc->dev);
/* Cases the state machine will not complete correctly without help */
- if ((tf->flags & ATA_TFLAG_LBA48) || tf->protocol == ATA_PROT_ATAPI_DMA)
- {
+ if ((tf->flags & ATA_TFLAG_LBA48) || tf->protocol == ATAPI_PROT_DMA) {
len = qc->nbytes / 2;
if (tf->flags & ATA_TFLAG_WRITE)
@@ -208,7 +207,7 @@ static void pdc2026x_bmdma_stop(struct ata_queued_cmd *qc)
void __iomem *atapi_reg = master + 0x20 + (4 * ap->port_no);
/* Cases the state machine will not complete correctly */
- if (tf->protocol == ATA_PROT_ATAPI_DMA || ( tf->flags & ATA_TFLAG_LBA48)) {
+ if (tf->protocol == ATAPI_PROT_DMA || (tf->flags & ATA_TFLAG_LBA48)) {
iowrite32(0, atapi_reg);
iowrite8(ioread8(clock) & ~sel66, clock);
}
diff --git a/trunk/drivers/ata/pata_qdi.c b/trunk/drivers/ata/pata_qdi.c
index a4c0e502cb42..9f308ed76cc8 100644
--- a/trunk/drivers/ata/pata_qdi.c
+++ b/trunk/drivers/ata/pata_qdi.c
@@ -124,29 +124,33 @@ static unsigned int qdi_qc_issue_prot(struct ata_queued_cmd *qc)
return ata_qc_issue_prot(qc);
}
-static void qdi_data_xfer(struct ata_device *adev, unsigned char *buf, unsigned int buflen, int write_data)
+static unsigned int qdi_data_xfer(struct ata_device *dev, unsigned char *buf,
+ unsigned int buflen, int rw)
{
- struct ata_port *ap = adev->link->ap;
- int slop = buflen & 3;
+ if (ata_id_has_dword_io(dev->id)) {
+ struct ata_port *ap = dev->link->ap;
+ int slop = buflen & 3;
- if (ata_id_has_dword_io(adev->id)) {
- if (write_data)
- iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
- else
+ if (rw == READ)
ioread32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
+ else
+ iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
if (unlikely(slop)) {
- __le32 pad = 0;
- if (write_data) {
- memcpy(&pad, buf + buflen - slop, slop);
- iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr);
- } else {
+ u32 pad;
+ if (rw == READ) {
pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr));
memcpy(buf + buflen - slop, &pad, slop);
+ } else {
+ memcpy(&pad, buf + buflen - slop, slop);
+ iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr);
}
+ buflen += 4 - slop;
}
} else
- ata_data_xfer(adev, buf, buflen, write_data);
+ buflen = ata_data_xfer(dev, buf, buflen, rw);
+
+ return buflen;
}
static struct scsi_host_template qdi_sht = {
diff --git a/trunk/drivers/ata/pata_scc.c b/trunk/drivers/ata/pata_scc.c
index ea2ef9fc15be..55055b27524c 100644
--- a/trunk/drivers/ata/pata_scc.c
+++ b/trunk/drivers/ata/pata_scc.c
@@ -768,45 +768,47 @@ static u8 scc_bmdma_status (struct ata_port *ap)
/**
* scc_data_xfer - Transfer data by PIO
- * @adev: device for this I/O
+ * @dev: device for this I/O
* @buf: data buffer
* @buflen: buffer length
- * @write_data: read/write
+ * @rw: read/write
*
* Note: Original code is ata_data_xfer().
*/
-static void scc_data_xfer (struct ata_device *adev, unsigned char *buf,
- unsigned int buflen, int write_data)
+static unsigned int scc_data_xfer (struct ata_device *dev, unsigned char *buf,
+ unsigned int buflen, int rw)
{
- struct ata_port *ap = adev->link->ap;
+ struct ata_port *ap = dev->link->ap;
unsigned int words = buflen >> 1;
unsigned int i;
u16 *buf16 = (u16 *) buf;
void __iomem *mmio = ap->ioaddr.data_addr;
/* Transfer multiple of 2 bytes */
- if (write_data) {
- for (i = 0; i < words; i++)
- out_be32(mmio, cpu_to_le16(buf16[i]));
- } else {
+ if (rw == READ)
for (i = 0; i < words; i++)
buf16[i] = le16_to_cpu(in_be32(mmio));
- }
+ else
+ for (i = 0; i < words; i++)
+ out_be32(mmio, cpu_to_le16(buf16[i]));
/* Transfer trailing 1 byte, if any. */
if (unlikely(buflen & 0x01)) {
u16 align_buf[1] = { 0 };
unsigned char *trailing_buf = buf + buflen - 1;
- if (write_data) {
- memcpy(align_buf, trailing_buf, 1);
- out_be32(mmio, cpu_to_le16(align_buf[0]));
- } else {
+ if (rw == READ) {
align_buf[0] = le16_to_cpu(in_be32(mmio));
memcpy(trailing_buf, align_buf, 1);
+ } else {
+ memcpy(align_buf, trailing_buf, 1);
+ out_be32(mmio, cpu_to_le16(align_buf[0]));
}
+ words++;
}
+
+ return words << 1;
}
/**
diff --git a/trunk/drivers/ata/pata_serverworks.c b/trunk/drivers/ata/pata_serverworks.c
index 8bed88873720..9c523fbf529e 100644
--- a/trunk/drivers/ata/pata_serverworks.c
+++ b/trunk/drivers/ata/pata_serverworks.c
@@ -41,7 +41,7 @@
#include
#define DRV_NAME "pata_serverworks"
-#define DRV_VERSION "0.4.2"
+#define DRV_VERSION "0.4.3"
#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
@@ -102,7 +102,7 @@ static int osb4_cable(struct ata_port *ap) {
}
/**
- * csb4_cable - CSB5/6 cable detect
+ * csb_cable - CSB5/6 cable detect
* @ap: ATA port to check
*
* Serverworks default arrangement is to use the drive side detection
@@ -110,7 +110,7 @@ static int osb4_cable(struct ata_port *ap) {
*/
static int csb_cable(struct ata_port *ap) {
- return ATA_CBL_PATA80;
+ return ATA_CBL_PATA_UNK;
}
struct sv_cable_table {
@@ -231,7 +231,6 @@ static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned lo
return ata_pci_default_filter(adev, mask);
}
-
/**
* serverworks_set_piomode - set initial PIO mode data
* @ap: ATA interface
@@ -243,7 +242,7 @@ static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned lo
static void serverworks_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
static const u8 pio_mode[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };
- int offset = 1 + (2 * ap->port_no) - adev->devno;
+ int offset = 1 + 2 * ap->port_no - adev->devno;
int devbits = (2 * ap->port_no + adev->devno) * 4;
u16 csb5_pio;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
diff --git a/trunk/drivers/ata/pata_via.c b/trunk/drivers/ata/pata_via.c
index 453d72bf2598..39627ab684bf 100644
--- a/trunk/drivers/ata/pata_via.c
+++ b/trunk/drivers/ata/pata_via.c
@@ -185,7 +185,8 @@ static int via_cable_detect(struct ata_port *ap) {
if (ata66 & (0x10100000 >> (16 * ap->port_no)))
return ATA_CBL_PATA80;
/* Check with ACPI so we can spot BIOS reported SATA bridges */
- if (ata_acpi_cbl_80wire(ap))
+ if (ata_acpi_init_gtm(ap) &&
+ ata_acpi_cbl_80wire(ap, ata_acpi_init_gtm(ap)))
return ATA_CBL_PATA80;
return ATA_CBL_PATA40;
}
diff --git a/trunk/drivers/ata/pata_winbond.c b/trunk/drivers/ata/pata_winbond.c
index 7116a9e7a8b2..99c92eda217b 100644
--- a/trunk/drivers/ata/pata_winbond.c
+++ b/trunk/drivers/ata/pata_winbond.c
@@ -92,29 +92,33 @@ static void winbond_set_piomode(struct ata_port *ap, struct ata_device *adev)
}
-static void winbond_data_xfer(struct ata_device *adev, unsigned char *buf, unsigned int buflen, int write_data)
+static unsigned int winbond_data_xfer(struct ata_device *dev,
+ unsigned char *buf, unsigned int buflen, int rw)
{
- struct ata_port *ap = adev->link->ap;
+ struct ata_port *ap = dev->link->ap;
int slop = buflen & 3;
- if (ata_id_has_dword_io(adev->id)) {
- if (write_data)
- iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
- else
+ if (ata_id_has_dword_io(dev->id)) {
+ if (rw == READ)
ioread32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
+ else
+ iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
if (unlikely(slop)) {
- __le32 pad = 0;
- if (write_data) {
- memcpy(&pad, buf + buflen - slop, slop);
- iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr);
- } else {
+ u32 pad;
+ if (rw == READ) {
pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr));
memcpy(buf + buflen - slop, &pad, slop);
+ } else {
+ memcpy(&pad, buf + buflen - slop, slop);
+ iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr);
}
+ buflen += 4 - slop;
}
} else
- ata_data_xfer(adev, buf, buflen, write_data);
+ buflen = ata_data_xfer(dev, buf, buflen, rw);
+
+ return buflen;
}
static struct scsi_host_template winbond_sht = {
@@ -191,7 +195,7 @@ static __init int winbond_init_one(unsigned long port)
reg = winbond_readcfg(port, 0x81);
if (!(reg & 0x03)) /* Disabled */
- return 0;
+ return -ENODEV;
for (i = 0; i < 2 ; i ++) {
unsigned long cmd_port = 0x1F0 - (0x80 * i);
diff --git a/trunk/drivers/ata/pdc_adma.c b/trunk/drivers/ata/pdc_adma.c
index bd4c2a3c88d7..8e1b7e9c0ae4 100644
--- a/trunk/drivers/ata/pdc_adma.c
+++ b/trunk/drivers/ata/pdc_adma.c
@@ -321,8 +321,9 @@ static int adma_fill_sg(struct ata_queued_cmd *qc)
u8 *buf = pp->pkt, *last_buf = NULL;
int i = (2 + buf[3]) * 8;
u8 pFLAGS = pORD | ((qc->tf.flags & ATA_TFLAG_WRITE) ? pDIRO : 0);
+ unsigned int si;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
u32 addr;
u32 len;
@@ -455,7 +456,7 @@ static unsigned int adma_qc_issue(struct ata_queued_cmd *qc)
adma_packet_start(qc);
return 0;
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
BUG();
break;
diff --git a/trunk/drivers/ata/sata_fsl.c b/trunk/drivers/ata/sata_fsl.c
index d015b4adcfe0..922d7b2efba8 100644
--- a/trunk/drivers/ata/sata_fsl.c
+++ b/trunk/drivers/ata/sata_fsl.c
@@ -333,13 +333,14 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
struct prde *prd_ptr_to_indirect_ext = NULL;
unsigned indirect_ext_segment_sz = 0;
dma_addr_t indirect_ext_segment_paddr;
+ unsigned int si;
VPRINTK("SATA FSL : cd = 0x%x, prd = 0x%x\n", cmd_desc, prd);
indirect_ext_segment_paddr = cmd_desc_paddr +
SATA_FSL_CMD_DESC_OFFSET_TO_PRDT + SATA_FSL_MAX_PRD_DIRECT * 16;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
dma_addr_t sg_addr = sg_dma_address(sg);
u32 sg_len = sg_dma_len(sg);
@@ -417,7 +418,7 @@ static void sata_fsl_qc_prep(struct ata_queued_cmd *qc)
}
/* setup "ACMD - atapi command" in cmd. desc. if this is ATAPI cmd */
- if (is_atapi_taskfile(&qc->tf)) {
+ if (ata_is_atapi(qc->tf.protocol)) {
desc_info |= ATAPI_CMD;
memset((void *)&cd->acmd, 0, 32);
memcpy((void *)&cd->acmd, qc->cdb, qc->dev->cdb_len);
diff --git a/trunk/drivers/ata/sata_inic162x.c b/trunk/drivers/ata/sata_inic162x.c
index 323c087e8cc1..96e614a1c169 100644
--- a/trunk/drivers/ata/sata_inic162x.c
+++ b/trunk/drivers/ata/sata_inic162x.c
@@ -585,7 +585,7 @@ static struct ata_port_operations inic_port_ops = {
};
static struct ata_port_info inic_port_info = {
- /* For some reason, ATA_PROT_ATAPI is broken on this
+ /* For some reason, ATAPI_PROT_PIO is broken on this
* controller, and no, PIO_POLLING does't fix it. It somehow
* manages to report the wrong ireason and ignoring ireason
* results in machine lock up. Tell libata to always prefer
diff --git a/trunk/drivers/ata/sata_mv.c b/trunk/drivers/ata/sata_mv.c
index 37b850ae0845..7e72463a90eb 100644
--- a/trunk/drivers/ata/sata_mv.c
+++ b/trunk/drivers/ata/sata_mv.c
@@ -1136,9 +1136,10 @@ static void mv_fill_sg(struct ata_queued_cmd *qc)
struct mv_port_priv *pp = qc->ap->private_data;
struct scatterlist *sg;
struct mv_sg *mv_sg, *last_sg = NULL;
+ unsigned int si;
mv_sg = pp->sg_tbl;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
dma_addr_t addr = sg_dma_address(sg);
u32 sg_len = sg_dma_len(sg);
diff --git a/trunk/drivers/ata/sata_nv.c b/trunk/drivers/ata/sata_nv.c
index ed5dc7cb50cd..a0f98fdab7a0 100644
--- a/trunk/drivers/ata/sata_nv.c
+++ b/trunk/drivers/ata/sata_nv.c
@@ -1336,21 +1336,18 @@ static void nv_adma_fill_aprd(struct ata_queued_cmd *qc,
static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb)
{
struct nv_adma_port_priv *pp = qc->ap->private_data;
- unsigned int idx;
struct nv_adma_prd *aprd;
struct scatterlist *sg;
+ unsigned int si;
VPRINTK("ENTER\n");
- idx = 0;
-
- ata_for_each_sg(sg, qc) {
- aprd = (idx < 5) ? &cpb->aprd[idx] :
- &pp->aprd[NV_ADMA_SGTBL_LEN * qc->tag + (idx-5)];
- nv_adma_fill_aprd(qc, sg, idx, aprd);
- idx++;
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
+ aprd = (si < 5) ? &cpb->aprd[si] :
+ &pp->aprd[NV_ADMA_SGTBL_LEN * qc->tag + (si-5)];
+ nv_adma_fill_aprd(qc, sg, si, aprd);
}
- if (idx > 5)
+ if (si > 5)
cpb->next_aprd = cpu_to_le64(((u64)(pp->aprd_dma + NV_ADMA_SGTBL_SZ * qc->tag)));
else
cpb->next_aprd = cpu_to_le64(0);
@@ -1995,17 +1992,14 @@ static void nv_swncq_fill_sg(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct scatterlist *sg;
- unsigned int idx;
struct nv_swncq_port_priv *pp = ap->private_data;
struct ata_prd *prd;
-
- WARN_ON(qc->__sg == NULL);
- WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
+ unsigned int si, idx;
prd = pp->prd + ATA_MAX_PRD * qc->tag;
idx = 0;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
u32 addr, offset;
u32 sg_len, len;
@@ -2027,8 +2021,7 @@ static void nv_swncq_fill_sg(struct ata_queued_cmd *qc)
}
}
- if (idx)
- prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+ prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
}
static unsigned int nv_swncq_issue_atacmd(struct ata_port *ap,
diff --git a/trunk/drivers/ata/sata_promise.c b/trunk/drivers/ata/sata_promise.c
index 7914def54fa3..a07d319f6e8c 100644
--- a/trunk/drivers/ata/sata_promise.c
+++ b/trunk/drivers/ata/sata_promise.c
@@ -450,19 +450,19 @@ static void pdc_atapi_pkt(struct ata_queued_cmd *qc)
struct pdc_port_priv *pp = ap->private_data;
u8 *buf = pp->pkt;
u32 *buf32 = (u32 *) buf;
- unsigned int dev_sel, feature, nbytes;
+ unsigned int dev_sel, feature;
/* set control bits (byte 0), zero delay seq id (byte 3),
* and seq id (byte 2)
*/
switch (qc->tf.protocol) {
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
if (!(qc->tf.flags & ATA_TFLAG_WRITE))
buf32[0] = cpu_to_le32(PDC_PKT_READ);
else
buf32[0] = 0;
break;
- case ATA_PROT_ATAPI_NODATA:
+ case ATAPI_PROT_NODATA:
buf32[0] = cpu_to_le32(PDC_PKT_NODATA);
break;
default:
@@ -473,45 +473,37 @@ static void pdc_atapi_pkt(struct ata_queued_cmd *qc)
buf32[2] = 0; /* no next-packet */
/* select drive */
- if (sata_scr_valid(&ap->link)) {
+ if (sata_scr_valid(&ap->link))
dev_sel = PDC_DEVICE_SATA;
- } else {
- dev_sel = ATA_DEVICE_OBS;
- if (qc->dev->devno != 0)
- dev_sel |= ATA_DEV1;
- }
+ else
+ dev_sel = qc->tf.device;
+
buf[12] = (1 << 5) | ATA_REG_DEVICE;
buf[13] = dev_sel;
buf[14] = (1 << 5) | ATA_REG_DEVICE | PDC_PKT_CLEAR_BSY;
buf[15] = dev_sel; /* once more, waiting for BSY to clear */
buf[16] = (1 << 5) | ATA_REG_NSECT;
- buf[17] = 0x00;
+ buf[17] = qc->tf.nsect;
buf[18] = (1 << 5) | ATA_REG_LBAL;
- buf[19] = 0x00;
+ buf[19] = qc->tf.lbal;
/* set feature and byte counter registers */
- if (qc->tf.protocol != ATA_PROT_ATAPI_DMA) {
+ if (qc->tf.protocol != ATAPI_PROT_DMA)
feature = PDC_FEATURE_ATAPI_PIO;
- /* set byte counter register to real transfer byte count */
- nbytes = qc->nbytes;
- if (nbytes > 0xffff)
- nbytes = 0xffff;
- } else {
+ else
feature = PDC_FEATURE_ATAPI_DMA;
- /* set byte counter register to 0 */
- nbytes = 0;
- }
+
buf[20] = (1 << 5) | ATA_REG_FEATURE;
buf[21] = feature;
buf[22] = (1 << 5) | ATA_REG_BYTEL;
- buf[23] = nbytes & 0xFF;
+ buf[23] = qc->tf.lbam;
buf[24] = (1 << 5) | ATA_REG_BYTEH;
- buf[25] = (nbytes >> 8) & 0xFF;
+ buf[25] = qc->tf.lbah;
/* send ATAPI packet command 0xA0 */
buf[26] = (1 << 5) | ATA_REG_CMD;
- buf[27] = ATA_CMD_PACKET;
+ buf[27] = qc->tf.command;
/* select drive and check DRQ */
buf[28] = (1 << 5) | ATA_REG_DEVICE | PDC_PKT_WAIT_DRDY;
@@ -541,17 +533,15 @@ static void pdc_fill_sg(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct scatterlist *sg;
- unsigned int idx;
const u32 SG_COUNT_ASIC_BUG = 41*4;
+ unsigned int si, idx;
+ u32 len;
if (!(qc->flags & ATA_QCFLAG_DMAMAP))
return;
- WARN_ON(qc->__sg == NULL);
- WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
-
idx = 0;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
u32 addr, offset;
u32 sg_len, len;
@@ -578,29 +568,27 @@ static void pdc_fill_sg(struct ata_queued_cmd *qc)
}
}
- if (idx) {
- u32 len = le32_to_cpu(ap->prd[idx - 1].flags_len);
+ len = le32_to_cpu(ap->prd[idx - 1].flags_len);
- if (len > SG_COUNT_ASIC_BUG) {
- u32 addr;
+ if (len > SG_COUNT_ASIC_BUG) {
+ u32 addr;
- VPRINTK("Splitting last PRD.\n");
+ VPRINTK("Splitting last PRD.\n");
- addr = le32_to_cpu(ap->prd[idx - 1].addr);
- ap->prd[idx - 1].flags_len = cpu_to_le32(len - SG_COUNT_ASIC_BUG);
- VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx - 1, addr, SG_COUNT_ASIC_BUG);
+ addr = le32_to_cpu(ap->prd[idx - 1].addr);
+ ap->prd[idx - 1].flags_len = cpu_to_le32(len - SG_COUNT_ASIC_BUG);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx - 1, addr, SG_COUNT_ASIC_BUG);
- addr = addr + len - SG_COUNT_ASIC_BUG;
- len = SG_COUNT_ASIC_BUG;
- ap->prd[idx].addr = cpu_to_le32(addr);
- ap->prd[idx].flags_len = cpu_to_le32(len);
- VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
+ addr = addr + len - SG_COUNT_ASIC_BUG;
+ len = SG_COUNT_ASIC_BUG;
+ ap->prd[idx].addr = cpu_to_le32(addr);
+ ap->prd[idx].flags_len = cpu_to_le32(len);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
- idx++;
- }
-
- ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+ idx++;
}
+
+ ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
}
static void pdc_qc_prep(struct ata_queued_cmd *qc)
@@ -627,14 +615,14 @@ static void pdc_qc_prep(struct ata_queued_cmd *qc)
pdc_pkt_footer(&qc->tf, pp->pkt, i);
break;
- case ATA_PROT_ATAPI:
+ case ATAPI_PROT_PIO:
pdc_fill_sg(qc);
break;
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
pdc_fill_sg(qc);
/*FALLTHROUGH*/
- case ATA_PROT_ATAPI_NODATA:
+ case ATAPI_PROT_NODATA:
pdc_atapi_pkt(qc);
break;
@@ -754,8 +742,8 @@ static inline unsigned int pdc_host_intr(struct ata_port *ap,
switch (qc->tf.protocol) {
case ATA_PROT_DMA:
case ATA_PROT_NODATA:
- case ATA_PROT_ATAPI_DMA:
- case ATA_PROT_ATAPI_NODATA:
+ case ATAPI_PROT_DMA:
+ case ATAPI_PROT_NODATA:
qc->err_mask |= ac_err_mask(ata_wait_idle(ap));
ata_qc_complete(qc);
handled = 1;
@@ -900,7 +888,7 @@ static inline void pdc_packet_start(struct ata_queued_cmd *qc)
static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc)
{
switch (qc->tf.protocol) {
- case ATA_PROT_ATAPI_NODATA:
+ case ATAPI_PROT_NODATA:
if (qc->dev->flags & ATA_DFLAG_CDB_INTR)
break;
/*FALLTHROUGH*/
@@ -908,7 +896,7 @@ static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc)
if (qc->tf.flags & ATA_TFLAG_POLLING)
break;
/*FALLTHROUGH*/
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
case ATA_PROT_DMA:
pdc_packet_start(qc);
return 0;
@@ -922,16 +910,14 @@ static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc)
static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
{
- WARN_ON(tf->protocol == ATA_PROT_DMA ||
- tf->protocol == ATA_PROT_ATAPI_DMA);
+ WARN_ON(tf->protocol == ATA_PROT_DMA || tf->protocol == ATAPI_PROT_DMA);
ata_tf_load(ap, tf);
}
static void pdc_exec_command_mmio(struct ata_port *ap,
const struct ata_taskfile *tf)
{
- WARN_ON(tf->protocol == ATA_PROT_DMA ||
- tf->protocol == ATA_PROT_ATAPI_DMA);
+ WARN_ON(tf->protocol == ATA_PROT_DMA || tf->protocol == ATAPI_PROT_DMA);
ata_exec_command(ap, tf);
}
diff --git a/trunk/drivers/ata/sata_promise.h b/trunk/drivers/ata/sata_promise.h
index 6ee5e190262d..00d6000e546f 100644
--- a/trunk/drivers/ata/sata_promise.h
+++ b/trunk/drivers/ata/sata_promise.h
@@ -46,7 +46,7 @@ static inline unsigned int pdc_pkt_header(struct ata_taskfile *tf,
unsigned int devno, u8 *buf)
{
u8 dev_reg;
- u32 *buf32 = (u32 *) buf;
+ __le32 *buf32 = (__le32 *) buf;
/* set control bits (byte 0), zero delay seq id (byte 3),
* and seq id (byte 2)
diff --git a/trunk/drivers/ata/sata_qstor.c b/trunk/drivers/ata/sata_qstor.c
index c68b241805fd..91cc12c82040 100644
--- a/trunk/drivers/ata/sata_qstor.c
+++ b/trunk/drivers/ata/sata_qstor.c
@@ -287,14 +287,10 @@ static unsigned int qs_fill_sg(struct ata_queued_cmd *qc)
struct scatterlist *sg;
struct ata_port *ap = qc->ap;
struct qs_port_priv *pp = ap->private_data;
- unsigned int nelem;
u8 *prd = pp->pkt + QS_CPB_BYTES;
+ unsigned int si;
- WARN_ON(qc->__sg == NULL);
- WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
-
- nelem = 0;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
u64 addr;
u32 len;
@@ -306,12 +302,11 @@ static unsigned int qs_fill_sg(struct ata_queued_cmd *qc)
*(__le32 *)prd = cpu_to_le32(len);
prd += sizeof(u64);
- VPRINTK("PRD[%u] = (0x%llX, 0x%X)\n", nelem,
+ VPRINTK("PRD[%u] = (0x%llX, 0x%X)\n", si,
(unsigned long long)addr, len);
- nelem++;
}
- return nelem;
+ return si;
}
static void qs_qc_prep(struct ata_queued_cmd *qc)
@@ -376,7 +371,7 @@ static unsigned int qs_qc_issue(struct ata_queued_cmd *qc)
qs_packet_start(qc);
return 0;
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
BUG();
break;
diff --git a/trunk/drivers/ata/sata_sil.c b/trunk/drivers/ata/sata_sil.c
index f5119bf40c24..0b8191b52f97 100644
--- a/trunk/drivers/ata/sata_sil.c
+++ b/trunk/drivers/ata/sata_sil.c
@@ -416,15 +416,14 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2)
*/
/* Check the ATA_DFLAG_CDB_INTR flag is enough here.
- * The flag was turned on only for atapi devices.
- * No need to check is_atapi_taskfile(&qc->tf) again.
+ * The flag was turned on only for atapi devices. No
+ * need to check ata_is_atapi(qc->tf.protocol) again.
*/
if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
goto err_hsm;
break;
case HSM_ST_LAST:
- if (qc->tf.protocol == ATA_PROT_DMA ||
- qc->tf.protocol == ATA_PROT_ATAPI_DMA) {
+ if (ata_is_dma(qc->tf.protocol)) {
/* clear DMA-Start bit */
ap->ops->bmdma_stop(qc);
@@ -451,8 +450,7 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2)
/* kick HSM in the ass */
ata_hsm_move(ap, qc, status, 0);
- if (unlikely(qc->err_mask) && (qc->tf.protocol == ATA_PROT_DMA ||
- qc->tf.protocol == ATA_PROT_ATAPI_DMA))
+ if (unlikely(qc->err_mask) && ata_is_dma(qc->tf.protocol))
ata_ehi_push_desc(ehi, "BMDMA2 stat 0x%x", bmdma2);
return;
diff --git a/trunk/drivers/ata/sata_sil24.c b/trunk/drivers/ata/sata_sil24.c
index 864c1c1b8511..b4b1f91ea693 100644
--- a/trunk/drivers/ata/sata_sil24.c
+++ b/trunk/drivers/ata/sata_sil24.c
@@ -813,8 +813,9 @@ static inline void sil24_fill_sg(struct ata_queued_cmd *qc,
{
struct scatterlist *sg;
struct sil24_sge *last_sge = NULL;
+ unsigned int si;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
sge->addr = cpu_to_le64(sg_dma_address(sg));
sge->cnt = cpu_to_le32(sg_dma_len(sg));
sge->flags = 0;
@@ -823,8 +824,7 @@ static inline void sil24_fill_sg(struct ata_queued_cmd *qc,
sge++;
}
- if (likely(last_sge))
- last_sge->flags = cpu_to_le32(SGE_TRM);
+ last_sge->flags = cpu_to_le32(SGE_TRM);
}
static int sil24_qc_defer(struct ata_queued_cmd *qc)
@@ -852,9 +852,7 @@ static int sil24_qc_defer(struct ata_queued_cmd *qc)
* set.
*
*/
- int is_excl = (prot == ATA_PROT_ATAPI ||
- prot == ATA_PROT_ATAPI_NODATA ||
- prot == ATA_PROT_ATAPI_DMA ||
+ int is_excl = (ata_is_atapi(prot) ||
(qc->flags & ATA_QCFLAG_RESULT_TF));
if (unlikely(ap->excl_link)) {
@@ -885,35 +883,21 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc)
cb = &pp->cmd_block[sil24_tag(qc->tag)];
- switch (qc->tf.protocol) {
- case ATA_PROT_PIO:
- case ATA_PROT_DMA:
- case ATA_PROT_NCQ:
- case ATA_PROT_NODATA:
+ if (!ata_is_atapi(qc->tf.protocol)) {
prb = &cb->ata.prb;
sge = cb->ata.sge;
- break;
-
- case ATA_PROT_ATAPI:
- case ATA_PROT_ATAPI_DMA:
- case ATA_PROT_ATAPI_NODATA:
+ } else {
prb = &cb->atapi.prb;
sge = cb->atapi.sge;
memset(cb->atapi.cdb, 0, 32);
memcpy(cb->atapi.cdb, qc->cdb, qc->dev->cdb_len);
- if (qc->tf.protocol != ATA_PROT_ATAPI_NODATA) {
+ if (ata_is_data(qc->tf.protocol)) {
if (qc->tf.flags & ATA_TFLAG_WRITE)
ctrl = PRB_CTRL_PACKET_WRITE;
else
ctrl = PRB_CTRL_PACKET_READ;
}
- break;
-
- default:
- prb = NULL; /* shut up, gcc */
- sge = NULL;
- BUG();
}
prb->ctrl = cpu_to_le16(ctrl);
diff --git a/trunk/drivers/ata/sata_sx4.c b/trunk/drivers/ata/sata_sx4.c
index 4d857185f33b..e3d56bc6726d 100644
--- a/trunk/drivers/ata/sata_sx4.c
+++ b/trunk/drivers/ata/sata_sx4.c
@@ -334,7 +334,7 @@ static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,
{
u32 addr;
unsigned int dw = PDC_DIMM_APKT_PRD >> 2;
- u32 *buf32 = (u32 *) buf;
+ __le32 *buf32 = (__le32 *) buf;
/* output ATA packet S/G table */
addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +
@@ -356,7 +356,7 @@ static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf,
{
u32 addr;
unsigned int dw = PDC_DIMM_HPKT_PRD >> 2;
- u32 *buf32 = (u32 *) buf;
+ __le32 *buf32 = (__le32 *) buf;
/* output Host DMA packet S/G table */
addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +
@@ -377,7 +377,7 @@ static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf,
unsigned int portno)
{
unsigned int i, dw;
- u32 *buf32 = (u32 *) buf;
+ __le32 *buf32 = (__le32 *) buf;
u8 dev_reg;
unsigned int dimm_sg = PDC_20621_DIMM_BASE +
@@ -429,7 +429,8 @@ static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf,
unsigned int portno)
{
unsigned int dw;
- u32 tmp, *buf32 = (u32 *) buf;
+ u32 tmp;
+ __le32 *buf32 = (__le32 *) buf;
unsigned int host_sg = PDC_20621_DIMM_BASE +
(PDC_DIMM_WINDOW_STEP * portno) +
@@ -473,7 +474,7 @@ static void pdc20621_dma_prep(struct ata_queued_cmd *qc)
void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR];
void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR];
unsigned int portno = ap->port_no;
- unsigned int i, idx, total_len = 0, sgt_len;
+ unsigned int i, si, idx, total_len = 0, sgt_len;
u32 *buf = (u32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ];
WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP));
@@ -487,7 +488,7 @@ static void pdc20621_dma_prep(struct ata_queued_cmd *qc)
* Build S/G table
*/
idx = 0;
- ata_for_each_sg(sg, qc) {
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
buf[idx++] = cpu_to_le32(sg_dma_address(sg));
buf[idx++] = cpu_to_le32(sg_dma_len(sg));
total_len += sg_dma_len(sg);
@@ -700,7 +701,7 @@ static unsigned int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc)
pdc20621_packet_start(qc);
return 0;
- case ATA_PROT_ATAPI_DMA:
+ case ATAPI_PROT_DMA:
BUG();
break;
diff --git a/trunk/drivers/ide/Kconfig b/trunk/drivers/ide/Kconfig
index fb06555708a8..ee01e273a537 100644
--- a/trunk/drivers/ide/Kconfig
+++ b/trunk/drivers/ide/Kconfig
@@ -374,17 +374,6 @@ comment "PCI IDE chipsets support"
config BLK_DEV_IDEPCI
bool
-config IDEPCI_SHARE_IRQ
- bool "Sharing PCI IDE interrupts support"
- depends on BLK_DEV_IDEPCI
- help
- Some ATA/IDE chipsets have hardware support which allows for
- sharing a single IRQ with other cards. To enable support for
- this in the ATA/IDE driver, say Y here.
-
- It is safe to say Y to this question, in most cases.
- If unsure, say N.
-
config IDEPCI_PCIBUS_ORDER
def_bool BLK_DEV_IDE=y && BLK_DEV_IDEPCI
@@ -707,7 +696,6 @@ config BLK_DEV_SVWKS
config BLK_DEV_SGIIOC4
tristate "Silicon Graphics IOC4 chipset ATA/ATAPI support"
depends on (IA64_SGI_SN2 || IA64_GENERIC) && SGI_IOC4
- select IDEPCI_SHARE_IRQ
select BLK_DEV_IDEDMA_PCI
help
This driver adds PIO & MultiMode DMA-2 support for the SGI IOC4
diff --git a/trunk/drivers/ide/arm/icside.c b/trunk/drivers/ide/arm/icside.c
index 93f71fcfc04d..673402f4a295 100644
--- a/trunk/drivers/ide/arm/icside.c
+++ b/trunk/drivers/ide/arm/icside.c
@@ -272,8 +272,6 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
case XFER_SW_DMA_0:
cycle_time = 480;
break;
- default:
- return;
}
/*
diff --git a/trunk/drivers/ide/cris/ide-cris.c b/trunk/drivers/ide/cris/ide-cris.c
index 476e0d65ed43..325e608d9e62 100644
--- a/trunk/drivers/ide/cris/ide-cris.c
+++ b/trunk/drivers/ide/cris/ide-cris.c
@@ -747,8 +747,6 @@ static void cris_set_dma_mode(ide_drive_t *drive, const u8 speed)
strobe = ATA_DMA2_STROBE;
hold = ATA_DMA2_HOLD;
break;
- default:
- return;
}
if (speed >= XFER_UDMA_0)
diff --git a/trunk/drivers/ide/ide-acpi.c b/trunk/drivers/ide/ide-acpi.c
index 899d56536e80..e0bb0cfa7bdd 100644
--- a/trunk/drivers/ide/ide-acpi.c
+++ b/trunk/drivers/ide/ide-acpi.c
@@ -383,27 +383,19 @@ static int taskfile_load_raw(ide_drive_t *drive,
gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]);
memset(&args, 0, sizeof(ide_task_t));
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.data_phase = TASKFILE_NO_DATA;
- args.handler = &task_no_data_intr;
/* convert gtf to IDE Taskfile */
- args.tfRegister[1] = gtf->tfa[0]; /* 0x1f1 */
- args.tfRegister[2] = gtf->tfa[1]; /* 0x1f2 */
- args.tfRegister[3] = gtf->tfa[2]; /* 0x1f3 */
- args.tfRegister[4] = gtf->tfa[3]; /* 0x1f4 */
- args.tfRegister[5] = gtf->tfa[4]; /* 0x1f5 */
- args.tfRegister[6] = gtf->tfa[5]; /* 0x1f6 */
- args.tfRegister[7] = gtf->tfa[6]; /* 0x1f7 */
+ memcpy(&args.tf_array[7], >f->tfa, 7);
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
if (ide_noacpitfs) {
DEBPRINT("_GTF execution disabled\n");
return err;
}
- err = ide_raw_taskfile(drive, &args, NULL);
+ err = ide_no_data_taskfile(drive, &args);
if (err)
- printk(KERN_ERR "%s: ide_raw_taskfile failed: %u\n",
+ printk(KERN_ERR "%s: ide_no_data_taskfile failed: %u\n",
__FUNCTION__, err);
return err;
diff --git a/trunk/drivers/ide/ide-cd.c b/trunk/drivers/ide/ide-cd.c
index c7d77f0ad892..44b033ec0ab0 100644
--- a/trunk/drivers/ide/ide-cd.c
+++ b/trunk/drivers/ide/ide-cd.c
@@ -917,19 +917,13 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive,
if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY))
return startstop;
+ /* FIXME: for Virtual DMA we must check harder */
if (info->dma)
info->dma = !hwif->dma_setup(drive);
/* Set up the controller registers. */
- /* FIXME: for Virtual DMA we must check harder */
- HWIF(drive)->OUTB(info->dma, IDE_FEATURE_REG);
- HWIF(drive)->OUTB(0, IDE_IREASON_REG);
- HWIF(drive)->OUTB(0, IDE_SECTOR_REG);
-
- HWIF(drive)->OUTB(xferlen & 0xff, IDE_BCOUNTL_REG);
- HWIF(drive)->OUTB(xferlen >> 8 , IDE_BCOUNTH_REG);
- if (IDE_CONTROL_REG)
- HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG);
+ ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL |
+ IDE_TFLAG_NO_SELECT_MASK, xferlen, info->dma);
if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
/* waiting for CDB interrupt, not DMA yet. */
diff --git a/trunk/drivers/ide/ide-disk.c b/trunk/drivers/ide/ide-disk.c
index b1781908e1f2..d8fdd865dea9 100644
--- a/trunk/drivers/ide/ide-disk.c
+++ b/trunk/drivers/ide/ide-disk.c
@@ -129,6 +129,50 @@ static int lba_capacity_is_ok (struct hd_driveid *id)
return 0; /* lba_capacity value may be bad */
}
+static const u8 ide_rw_cmds[] = {
+ WIN_MULTREAD,
+ WIN_MULTWRITE,
+ WIN_MULTREAD_EXT,
+ WIN_MULTWRITE_EXT,
+ WIN_READ,
+ WIN_WRITE,
+ WIN_READ_EXT,
+ WIN_WRITE_EXT,
+ WIN_READDMA,
+ WIN_WRITEDMA,
+ WIN_READDMA_EXT,
+ WIN_WRITEDMA_EXT,
+};
+
+static const u8 ide_data_phases[] = {
+ TASKFILE_MULTI_IN,
+ TASKFILE_MULTI_OUT,
+ TASKFILE_IN,
+ TASKFILE_OUT,
+ TASKFILE_IN_DMA,
+ TASKFILE_OUT_DMA,
+};
+
+static void ide_tf_set_cmd(ide_drive_t *drive, ide_task_t *task, u8 dma)
+{
+ u8 index, lba48, write;
+
+ lba48 = (task->tf_flags & IDE_TFLAG_LBA48) ? 2 : 0;
+ write = (task->tf_flags & IDE_TFLAG_WRITE) ? 1 : 0;
+
+ if (dma)
+ index = drive->vdma ? 4 : 8;
+ else
+ index = drive->mult_count ? 0 : 4;
+
+ task->tf.command = ide_rw_cmds[index + lba48 + write];
+
+ if (dma)
+ index = 8; /* fixup index */
+
+ task->data_phase = ide_data_phases[index / 2 + write];
+}
+
/*
* __ide_do_rw_disk() issues READ and WRITE commands to a disk,
* using LBA if supported, or CHS otherwise, to address sectors.
@@ -137,11 +181,11 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
{
ide_hwif_t *hwif = HWIF(drive);
unsigned int dma = drive->using_dma;
+ u16 nsectors = (u16)rq->nr_sectors;
u8 lba48 = (drive->addressing == 1) ? 1 : 0;
- task_ioreg_t command = WIN_NOP;
- ata_nsector_t nsectors;
-
- nsectors.all = (u16) rq->nr_sectors;
+ ide_task_t task;
+ struct ide_taskfile *tf = &task.tf;
+ ide_startstop_t rc;
if ((hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) && lba48 && dma) {
if (block + rq->nr_sectors > 1ULL << 28)
@@ -155,121 +199,76 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
ide_map_sg(drive, rq);
}
- if (IDE_CONTROL_REG)
- hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
-
- /* FIXME: SELECT_MASK(drive, 0) ? */
+ memset(&task, 0, sizeof(task));
+ task.tf_flags = IDE_TFLAG_NO_SELECT_MASK; /* FIXME? */
+ task.tf_flags |= (IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE);
if (drive->select.b.lba) {
if (lba48) {
- task_ioreg_t tasklets[10];
-
pr_debug("%s: LBA=0x%012llx\n", drive->name,
(unsigned long long)block);
- tasklets[0] = 0;
- tasklets[1] = 0;
- tasklets[2] = nsectors.b.low;
- tasklets[3] = nsectors.b.high;
- tasklets[4] = (task_ioreg_t) block;
- tasklets[5] = (task_ioreg_t) (block>>8);
- tasklets[6] = (task_ioreg_t) (block>>16);
- tasklets[7] = (task_ioreg_t) (block>>24);
- if (sizeof(block) == 4) {
- tasklets[8] = (task_ioreg_t) 0;
- tasklets[9] = (task_ioreg_t) 0;
- } else {
- tasklets[8] = (task_ioreg_t)((u64)block >> 32);
- tasklets[9] = (task_ioreg_t)((u64)block >> 40);
+ tf->hob_nsect = (nsectors >> 8) & 0xff;
+ tf->hob_lbal = (u8)(block >> 24);
+ if (sizeof(block) != 4) {
+ tf->hob_lbam = (u8)((u64)block >> 32);
+ tf->hob_lbah = (u8)((u64)block >> 40);
}
+
+ tf->nsect = nsectors & 0xff;
+ tf->lbal = (u8) block;
+ tf->lbam = (u8)(block >> 8);
+ tf->lbah = (u8)(block >> 16);
#ifdef DEBUG
printk("%s: 0x%02x%02x 0x%02x%02x%02x%02x%02x%02x\n",
- drive->name, tasklets[3], tasklets[2],
- tasklets[9], tasklets[8], tasklets[7],
- tasklets[6], tasklets[5], tasklets[4]);
+ drive->name, tf->hob_nsect, tf->nsect,
+ tf->hob_lbah, tf->hob_lbam, tf->hob_lbal,
+ tf->lbah, tf->lbam, tf->lbal);
#endif
- hwif->OUTB(tasklets[1], IDE_FEATURE_REG);
- hwif->OUTB(tasklets[3], IDE_NSECTOR_REG);
- hwif->OUTB(tasklets[7], IDE_SECTOR_REG);
- hwif->OUTB(tasklets[8], IDE_LCYL_REG);
- hwif->OUTB(tasklets[9], IDE_HCYL_REG);
-
- hwif->OUTB(tasklets[0], IDE_FEATURE_REG);
- hwif->OUTB(tasklets[2], IDE_NSECTOR_REG);
- hwif->OUTB(tasklets[4], IDE_SECTOR_REG);
- hwif->OUTB(tasklets[5], IDE_LCYL_REG);
- hwif->OUTB(tasklets[6], IDE_HCYL_REG);
- hwif->OUTB(0x00|drive->select.all,IDE_SELECT_REG);
+ task.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_OUT_HOB);
} else {
- hwif->OUTB(0x00, IDE_FEATURE_REG);
- hwif->OUTB(nsectors.b.low, IDE_NSECTOR_REG);
- hwif->OUTB(block, IDE_SECTOR_REG);
- hwif->OUTB(block>>=8, IDE_LCYL_REG);
- hwif->OUTB(block>>=8, IDE_HCYL_REG);
- hwif->OUTB(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
+ tf->nsect = nsectors & 0xff;
+ tf->lbal = block;
+ tf->lbam = block >>= 8;
+ tf->lbah = block >>= 8;
+ tf->device = (block >> 8) & 0xf;
}
} else {
unsigned int sect,head,cyl,track;
track = (int)block / drive->sect;
sect = (int)block % drive->sect + 1;
- hwif->OUTB(sect, IDE_SECTOR_REG);
head = track % drive->head;
cyl = track / drive->head;
pr_debug("%s: CHS=%u/%u/%u\n", drive->name, cyl, head, sect);
- hwif->OUTB(0x00, IDE_FEATURE_REG);
- hwif->OUTB(nsectors.b.low, IDE_NSECTOR_REG);
- hwif->OUTB(cyl, IDE_LCYL_REG);
- hwif->OUTB(cyl>>8, IDE_HCYL_REG);
- hwif->OUTB(head|drive->select.all,IDE_SELECT_REG);
+ tf->nsect = nsectors & 0xff;
+ tf->lbal = sect;
+ tf->lbam = cyl;
+ tf->lbah = cyl >> 8;
+ tf->device = head;
}
- if (dma) {
- if (!hwif->dma_setup(drive)) {
- if (rq_data_dir(rq)) {
- command = lba48 ? WIN_WRITEDMA_EXT : WIN_WRITEDMA;
- if (drive->vdma)
- command = lba48 ? WIN_WRITE_EXT: WIN_WRITE;
- } else {
- command = lba48 ? WIN_READDMA_EXT : WIN_READDMA;
- if (drive->vdma)
- command = lba48 ? WIN_READ_EXT: WIN_READ;
- }
- hwif->dma_exec_cmd(drive, command);
- hwif->dma_start(drive);
- return ide_started;
- }
- /* fallback to PIO */
- ide_init_sg_cmd(drive, rq);
- }
+ if (rq_data_dir(rq))
+ task.tf_flags |= IDE_TFLAG_WRITE;
- if (rq_data_dir(rq) == READ) {
+ ide_tf_set_cmd(drive, &task, dma);
+ if (!dma)
+ hwif->data_phase = task.data_phase;
+ task.rq = rq;
- if (drive->mult_count) {
- hwif->data_phase = TASKFILE_MULTI_IN;
- command = lba48 ? WIN_MULTREAD_EXT : WIN_MULTREAD;
- } else {
- hwif->data_phase = TASKFILE_IN;
- command = lba48 ? WIN_READ_EXT : WIN_READ;
- }
+ rc = do_rw_taskfile(drive, &task);
- ide_execute_command(drive, command, &task_in_intr, WAIT_CMD, NULL);
- return ide_started;
- } else {
- if (drive->mult_count) {
- hwif->data_phase = TASKFILE_MULTI_OUT;
- command = lba48 ? WIN_MULTWRITE_EXT : WIN_MULTWRITE;
- } else {
- hwif->data_phase = TASKFILE_OUT;
- command = lba48 ? WIN_WRITE_EXT : WIN_WRITE;
- }
-
- /* FIXME: ->OUTBSYNC ? */
- hwif->OUTB(command, IDE_COMMAND_REG);
-
- return pre_task_out_intr(drive, rq);
+ if (rc == ide_stopped && dma) {
+ /* fallback to PIO */
+ task.tf_flags |= IDE_TFLAG_DMA_PIO_FALLBACK;
+ ide_tf_set_cmd(drive, &task, 0);
+ hwif->data_phase = task.data_phase;
+ ide_init_sg_cmd(drive, rq);
+ rc = do_rw_taskfile(drive, &task);
}
+
+ return rc;
}
/*
@@ -307,57 +306,29 @@ static ide_startstop_t ide_do_rw_disk (ide_drive_t *drive, struct request *rq, s
* Queries for true maximum capacity of the drive.
* Returns maximum LBA address (> 0) of the drive, 0 if failed.
*/
-static unsigned long idedisk_read_native_max_address(ide_drive_t *drive)
+static u64 idedisk_read_native_max_address(ide_drive_t *drive, int lba48)
{
ide_task_t args;
- unsigned long addr = 0;
+ struct ide_taskfile *tf = &args.tf;
+ u64 addr = 0;
/* Create IDE/ATA command request structure */
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_SELECT_OFFSET] = 0x40;
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_READ_NATIVE_MAX;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
+ if (lba48)
+ tf->command = WIN_READ_NATIVE_MAX_EXT;
+ else
+ tf->command = WIN_READ_NATIVE_MAX;
+ tf->device = ATA_LBA;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ if (lba48)
+ args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_OUT_HOB);
/* submit command request */
- ide_raw_taskfile(drive, &args, NULL);
+ ide_no_data_taskfile(drive, &args);
/* if OK, compute maximum address value */
- if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) {
- addr = ((args.tfRegister[IDE_SELECT_OFFSET] & 0x0f) << 24)
- | ((args.tfRegister[ IDE_HCYL_OFFSET] ) << 16)
- | ((args.tfRegister[ IDE_LCYL_OFFSET] ) << 8)
- | ((args.tfRegister[IDE_SECTOR_OFFSET] ));
- addr++; /* since the return value is (maxlba - 1), we add 1 */
- }
- return addr;
-}
-
-static unsigned long long idedisk_read_native_max_address_ext(ide_drive_t *drive)
-{
- ide_task_t args;
- unsigned long long addr = 0;
-
- /* Create IDE/ATA command request structure */
- memset(&args, 0, sizeof(ide_task_t));
+ if ((tf->status & 0x01) == 0)
+ addr = ide_get_lba_addr(tf, lba48) + 1;
- args.tfRegister[IDE_SELECT_OFFSET] = 0x40;
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_READ_NATIVE_MAX_EXT;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
- /* submit command request */
- ide_raw_taskfile(drive, &args, NULL);
-
- /* if OK, compute maximum address value */
- if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) {
- u32 high = (args.hobRegister[IDE_HCYL_OFFSET] << 16) |
- (args.hobRegister[IDE_LCYL_OFFSET] << 8) |
- args.hobRegister[IDE_SECTOR_OFFSET];
- u32 low = ((args.tfRegister[IDE_HCYL_OFFSET])<<16) |
- ((args.tfRegister[IDE_LCYL_OFFSET])<<8) |
- (args.tfRegister[IDE_SECTOR_OFFSET]);
- addr = ((__u64)high << 24) | low;
- addr++; /* since the return value is (maxlba - 1), we add 1 */
- }
return addr;
}
@@ -365,67 +336,37 @@ static unsigned long long idedisk_read_native_max_address_ext(ide_drive_t *drive
* Sets maximum virtual LBA address of the drive.
* Returns new maximum virtual LBA address (> 0) or 0 on failure.
*/
-static unsigned long idedisk_set_max_address(ide_drive_t *drive, unsigned long addr_req)
+static u64 idedisk_set_max_address(ide_drive_t *drive, u64 addr_req, int lba48)
{
ide_task_t args;
- unsigned long addr_set = 0;
-
- addr_req--;
- /* Create IDE/ATA command request structure */
- memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_SECTOR_OFFSET] = ((addr_req >> 0) & 0xff);
- args.tfRegister[IDE_LCYL_OFFSET] = ((addr_req >> 8) & 0xff);
- args.tfRegister[IDE_HCYL_OFFSET] = ((addr_req >> 16) & 0xff);
- args.tfRegister[IDE_SELECT_OFFSET] = ((addr_req >> 24) & 0x0f) | 0x40;
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SET_MAX;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
- /* submit command request */
- ide_raw_taskfile(drive, &args, NULL);
- /* if OK, read new maximum address value */
- if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) {
- addr_set = ((args.tfRegister[IDE_SELECT_OFFSET] & 0x0f) << 24)
- | ((args.tfRegister[ IDE_HCYL_OFFSET] ) << 16)
- | ((args.tfRegister[ IDE_LCYL_OFFSET] ) << 8)
- | ((args.tfRegister[IDE_SECTOR_OFFSET] ));
- addr_set++;
- }
- return addr_set;
-}
-
-static unsigned long long idedisk_set_max_address_ext(ide_drive_t *drive, unsigned long long addr_req)
-{
- ide_task_t args;
- unsigned long long addr_set = 0;
+ struct ide_taskfile *tf = &args.tf;
+ u64 addr_set = 0;
addr_req--;
/* Create IDE/ATA command request structure */
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_SECTOR_OFFSET] = ((addr_req >> 0) & 0xff);
- args.tfRegister[IDE_LCYL_OFFSET] = ((addr_req >>= 8) & 0xff);
- args.tfRegister[IDE_HCYL_OFFSET] = ((addr_req >>= 8) & 0xff);
- args.tfRegister[IDE_SELECT_OFFSET] = 0x40;
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SET_MAX_EXT;
- args.hobRegister[IDE_SECTOR_OFFSET] = (addr_req >>= 8) & 0xff;
- args.hobRegister[IDE_LCYL_OFFSET] = (addr_req >>= 8) & 0xff;
- args.hobRegister[IDE_HCYL_OFFSET] = (addr_req >>= 8) & 0xff;
- args.hobRegister[IDE_SELECT_OFFSET] = 0x40;
- args.hobRegister[IDE_CONTROL_OFFSET_HOB]= (drive->ctl|0x80);
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
+ tf->lbal = (addr_req >> 0) & 0xff;
+ tf->lbam = (addr_req >>= 8) & 0xff;
+ tf->lbah = (addr_req >>= 8) & 0xff;
+ if (lba48) {
+ tf->hob_lbal = (addr_req >>= 8) & 0xff;
+ tf->hob_lbam = (addr_req >>= 8) & 0xff;
+ tf->hob_lbah = (addr_req >>= 8) & 0xff;
+ tf->command = WIN_SET_MAX_EXT;
+ } else {
+ tf->device = (addr_req >>= 8) & 0x0f;
+ tf->command = WIN_SET_MAX;
+ }
+ tf->device |= ATA_LBA;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ if (lba48)
+ args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_OUT_HOB);
/* submit command request */
- ide_raw_taskfile(drive, &args, NULL);
+ ide_no_data_taskfile(drive, &args);
/* if OK, compute maximum address value */
- if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) {
- u32 high = (args.hobRegister[IDE_HCYL_OFFSET] << 16) |
- (args.hobRegister[IDE_LCYL_OFFSET] << 8) |
- args.hobRegister[IDE_SECTOR_OFFSET];
- u32 low = ((args.tfRegister[IDE_HCYL_OFFSET])<<16) |
- ((args.tfRegister[IDE_LCYL_OFFSET])<<8) |
- (args.tfRegister[IDE_SECTOR_OFFSET]);
- addr_set = ((__u64)high << 24) | low;
- addr_set++;
- }
+ if ((tf->status & 0x01) == 0)
+ addr_set = ide_get_lba_addr(tf, lba48) + 1;
+
return addr_set;
}
@@ -471,10 +412,8 @@ static void idedisk_check_hpa(ide_drive_t *drive)
int lba48 = idedisk_supports_lba48(drive->id);
capacity = drive->capacity64;
- if (lba48)
- set_max = idedisk_read_native_max_address_ext(drive);
- else
- set_max = idedisk_read_native_max_address(drive);
+
+ set_max = idedisk_read_native_max_address(drive, lba48);
if (ide_in_drive_list(drive->id, hpa_list)) {
/*
@@ -495,10 +434,8 @@ static void idedisk_check_hpa(ide_drive_t *drive)
capacity, sectors_to_MB(capacity),
set_max, sectors_to_MB(set_max));
- if (lba48)
- set_max = idedisk_set_max_address_ext(drive, set_max);
- else
- set_max = idedisk_set_max_address(drive, set_max);
+ set_max = idedisk_set_max_address(drive, set_max, lba48);
+
if (set_max) {
drive->capacity64 = set_max;
printk(KERN_INFO "%s: Host Protected Area disabled.\n",
@@ -556,32 +493,32 @@ static sector_t idedisk_capacity (ide_drive_t *drive)
static int smart_enable(ide_drive_t *drive)
{
ide_task_t args;
+ struct ide_taskfile *tf = &args.tf;
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_FEATURE_OFFSET] = SMART_ENABLE;
- args.tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS;
- args.tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS;
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SMART;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
- return ide_raw_taskfile(drive, &args, NULL);
+ tf->feature = SMART_ENABLE;
+ tf->lbam = SMART_LCYL_PASS;
+ tf->lbah = SMART_HCYL_PASS;
+ tf->command = WIN_SMART;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ return ide_no_data_taskfile(drive, &args);
}
static int get_smart_data(ide_drive_t *drive, u8 *buf, u8 sub_cmd)
{
ide_task_t args;
+ struct ide_taskfile *tf = &args.tf;
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_FEATURE_OFFSET] = sub_cmd;
- args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01;
- args.tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS;
- args.tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS;
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SMART;
- args.command_type = IDE_DRIVE_TASK_IN;
- args.data_phase = TASKFILE_IN;
- args.handler = &task_in_intr;
+ tf->feature = sub_cmd;
+ tf->nsect = 0x01;
+ tf->lbam = SMART_LCYL_PASS;
+ tf->lbah = SMART_HCYL_PASS;
+ tf->command = WIN_SMART;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ args.data_phase = TASKFILE_IN;
(void) smart_enable(drive);
- return ide_raw_taskfile(drive, &args, buf);
+ return ide_raw_taskfile(drive, &args, buf, 1);
}
static int proc_idedisk_read_cache
@@ -659,19 +596,20 @@ static ide_proc_entry_t idedisk_proc[] = {
static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
{
ide_drive_t *drive = q->queuedata;
+ ide_task_t task;
- memset(rq->cmd, 0, sizeof(rq->cmd));
-
+ memset(&task, 0, sizeof(task));
if (ide_id_has_flush_cache_ext(drive->id) &&
(drive->capacity64 >= (1UL << 28)))
- rq->cmd[0] = WIN_FLUSH_CACHE_EXT;
+ task.tf.command = WIN_FLUSH_CACHE_EXT;
else
- rq->cmd[0] = WIN_FLUSH_CACHE;
-
+ task.tf.command = WIN_FLUSH_CACHE;
+ task.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ task.data_phase = TASKFILE_NO_DATA;
- rq->cmd_type = REQ_TYPE_ATA_TASK;
+ rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
rq->cmd_flags |= REQ_SOFTBARRIER;
- rq->buffer = rq->cmd;
+ rq->special = &task;
}
/*
@@ -753,12 +691,11 @@ static int write_cache(ide_drive_t *drive, int arg)
if (ide_id_has_flush_cache(drive->id)) {
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_FEATURE_OFFSET] = (arg) ?
+ args.tf.feature = arg ?
SETFEATURES_EN_WCACHE : SETFEATURES_DIS_WCACHE;
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
- err = ide_raw_taskfile(drive, &args, NULL);
+ args.tf.command = WIN_SETFEATURES;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ err = ide_no_data_taskfile(drive, &args);
if (err == 0)
drive->wcache = arg;
}
@@ -774,12 +711,11 @@ static int do_idedisk_flushcache (ide_drive_t *drive)
memset(&args, 0, sizeof(ide_task_t));
if (ide_id_has_flush_cache_ext(drive->id))
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT;
+ args.tf.command = WIN_FLUSH_CACHE_EXT;
else
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
- return ide_raw_taskfile(drive, &args, NULL);
+ args.tf.command = WIN_FLUSH_CACHE;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ return ide_no_data_taskfile(drive, &args);
}
static int set_acoustic (ide_drive_t *drive, int arg)
@@ -790,13 +726,11 @@ static int set_acoustic (ide_drive_t *drive, int arg)
return -EINVAL;
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_FEATURE_OFFSET] = (arg) ? SETFEATURES_EN_AAM :
- SETFEATURES_DIS_AAM;
- args.tfRegister[IDE_NSECTOR_OFFSET] = arg;
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
- ide_raw_taskfile(drive, &args, NULL);
+ args.tf.feature = arg ? SETFEATURES_EN_AAM : SETFEATURES_DIS_AAM;
+ args.tf.nsect = arg;
+ args.tf.command = WIN_SETFEATURES;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ ide_no_data_taskfile(drive, &args);
drive->acoustic = arg;
return 0;
}
@@ -1057,16 +991,15 @@ static int idedisk_open(struct inode *inode, struct file *filp)
if (drive->removable && idkp->openers == 1) {
ide_task_t args;
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_DOORLOCK;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
+ args.tf.command = WIN_DOORLOCK;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
check_disk_change(inode->i_bdev);
/*
* Ignore the return code from door_lock,
* since the open() has already succeeded,
* and the door_lock is irrelevant at this point.
*/
- if (drive->doorlocking && ide_raw_taskfile(drive, &args, NULL))
+ if (drive->doorlocking && ide_no_data_taskfile(drive, &args))
drive->doorlocking = 0;
}
return 0;
@@ -1084,10 +1017,9 @@ static int idedisk_release(struct inode *inode, struct file *filp)
if (drive->removable && idkp->openers == 1) {
ide_task_t args;
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_DOORUNLOCK;
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
- args.handler = &task_no_data_intr;
- if (drive->doorlocking && ide_raw_taskfile(drive, &args, NULL))
+ args.tf.command = WIN_DOORUNLOCK;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ if (drive->doorlocking && ide_no_data_taskfile(drive, &args))
drive->doorlocking = 0;
}
diff --git a/trunk/drivers/ide/ide-dma.c b/trunk/drivers/ide/ide-dma.c
index 4703837bf1fc..18c78ad2b31e 100644
--- a/trunk/drivers/ide/ide-dma.c
+++ b/trunk/drivers/ide/ide-dma.c
@@ -491,10 +491,6 @@ EXPORT_SYMBOL(ide_dma_host_on);
int __ide_dma_on (ide_drive_t *drive)
{
- /* consult the list of known "bad" drives */
- if (__ide_dma_bad_drive(drive))
- return 1;
-
drive->using_dma = 1;
ide_toggle_bounce(drive, 1);
@@ -827,22 +823,19 @@ int ide_set_dma(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
int rc;
- rc = ide_dma_check(drive);
+ /*
+ * Force DMAing for the beginning of the check.
+ * Some chipsets appear to do interesting
+ * things, if not checked and cleared.
+ * PARANOIA!!!
+ */
+ hwif->dma_off_quietly(drive);
- switch(rc) {
- case -1: /* DMA needs to be disabled */
- hwif->dma_off_quietly(drive);
- return -1;
- case 0: /* DMA needs to be enabled */
- return hwif->ide_dma_on(drive);
- case 1: /* DMA setting cannot be changed */
- break;
- default:
- BUG();
- break;
- }
+ rc = ide_dma_check(drive);
+ if (rc)
+ return rc;
- return rc;
+ return hwif->ide_dma_on(drive);
}
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
@@ -968,11 +961,6 @@ void ide_setup_dma(ide_hwif_t *hwif, unsigned long base, unsigned num_ports)
hwif->dma_base = base;
- if (hwif->mate)
- hwif->dma_master = hwif->channel ? hwif->mate->dma_base : base;
- else
- hwif->dma_master = base;
-
if (!(hwif->dma_command))
hwif->dma_command = hwif->dma_base;
if (!(hwif->dma_vendor1))
@@ -1014,8 +1002,6 @@ void ide_setup_dma(ide_hwif_t *hwif, unsigned long base, unsigned num_ports)
hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
}
printk("\n");
-
- BUG_ON(!hwif->dma_master);
}
EXPORT_SYMBOL_GPL(ide_setup_dma);
diff --git a/trunk/drivers/ide/ide-floppy.c b/trunk/drivers/ide/ide-floppy.c
index 04a357808f2e..ff8232ef9659 100644
--- a/trunk/drivers/ide/ide-floppy.c
+++ b/trunk/drivers/ide/ide-floppy.c
@@ -369,27 +369,6 @@ typedef struct ide_floppy_obj {
#define IDEFLOPPY_IOCTL_FORMAT_START 0x4602
#define IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS 0x4603
-#if 0
-/*
- * Special requests for our block device strategy routine.
- */
-#define IDEFLOPPY_FIRST_RQ 90
-
-/*
- * IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
- */
-#define IDEFLOPPY_PC_RQ 90
-
-#define IDEFLOPPY_LAST_RQ 90
-
-/*
- * A macro which can be used to check if a given request command
- * originated in the driver or in the buffer cache layer.
- */
-#define IDEFLOPPY_RQ_CMD(cmd) ((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
-
-#endif
-
/*
* Error codes which are returned in rq->errors to the higher part
* of the driver.
@@ -793,9 +772,8 @@ static void idefloppy_retry_pc (ide_drive_t *drive)
{
idefloppy_pc_t *pc;
struct request *rq;
- atapi_error_t error;
- error.all = HWIF(drive)->INB(IDE_ERROR_REG);
+ (void)drive->hwif->INB(IDE_ERROR_REG);
pc = idefloppy_next_pc_storage(drive);
rq = idefloppy_next_rq_storage(drive);
idefloppy_create_request_sense_cmd(pc);
@@ -809,12 +787,12 @@ static void idefloppy_retry_pc (ide_drive_t *drive)
static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
{
idefloppy_floppy_t *floppy = drive->driver_data;
- atapi_status_t status;
- atapi_bcount_t bcount;
- atapi_ireason_t ireason;
+ ide_hwif_t *hwif = drive->hwif;
idefloppy_pc_t *pc = floppy->pc;
struct request *rq = pc->rq;
unsigned int temp;
+ u16 bcount;
+ u8 stat, ireason;
debug_log(KERN_INFO "ide-floppy: Reached %s interrupt handler\n",
__FUNCTION__);
@@ -830,16 +808,16 @@ static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
}
/* Clear the interrupt */
- status.all = HWIF(drive)->INB(IDE_STATUS_REG);
+ stat = drive->hwif->INB(IDE_STATUS_REG);
- if (!status.b.drq) { /* No more interrupts */
+ if ((stat & DRQ_STAT) == 0) { /* No more interrupts */
debug_log(KERN_INFO "Packet command completed, %d bytes "
"transferred\n", pc->actually_transferred);
clear_bit(PC_DMA_IN_PROGRESS, &pc->flags);
local_irq_enable_in_hardirq();
- if (status.b.check || test_bit(PC_DMA_ERROR, &pc->flags)) {
+ if ((stat & ERR_STAT) || test_bit(PC_DMA_ERROR, &pc->flags)) {
/* Error detected */
debug_log(KERN_INFO "ide-floppy: %s: I/O error\n",
drive->name);
@@ -870,32 +848,32 @@ static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
}
/* Get the number of bytes to transfer */
- bcount.b.high = HWIF(drive)->INB(IDE_BCOUNTH_REG);
- bcount.b.low = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+ bcount = (hwif->INB(IDE_BCOUNTH_REG) << 8) |
+ hwif->INB(IDE_BCOUNTL_REG);
/* on this interrupt */
- ireason.all = HWIF(drive)->INB(IDE_IREASON_REG);
+ ireason = hwif->INB(IDE_IREASON_REG);
- if (ireason.b.cod) {
+ if (ireason & CD) {
printk(KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
return ide_do_reset(drive);
}
- if (ireason.b.io == test_bit(PC_WRITING, &pc->flags)) {
+ if (((ireason & IO) == IO) == test_bit(PC_WRITING, &pc->flags)) {
/* Hopefully, we will never get here */
printk(KERN_ERR "ide-floppy: We wanted to %s, ",
- ireason.b.io ? "Write":"Read");
+ (ireason & IO) ? "Write" : "Read");
printk(KERN_ERR "but the floppy wants us to %s !\n",
- ireason.b.io ? "Read":"Write");
+ (ireason & IO) ? "Read" : "Write");
return ide_do_reset(drive);
}
if (!test_bit(PC_WRITING, &pc->flags)) {
/* Reading - Check that we have enough space */
- temp = pc->actually_transferred + bcount.all;
+ temp = pc->actually_transferred + bcount;
if (temp > pc->request_transfer) {
if (temp > pc->buffer_size) {
printk(KERN_ERR "ide-floppy: The floppy wants "
"to send us more data than expected "
"- discarding data\n");
- idefloppy_discard_data(drive,bcount.all);
+ idefloppy_discard_data(drive, bcount);
BUG_ON(HWGROUP(drive)->handler != NULL);
ide_set_handler(drive,
&idefloppy_pc_intr,
@@ -911,23 +889,21 @@ static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
if (test_bit(PC_WRITING, &pc->flags)) {
if (pc->buffer != NULL)
/* Write the current buffer */
- HWIF(drive)->atapi_output_bytes(drive,
- pc->current_position,
- bcount.all);
+ hwif->atapi_output_bytes(drive, pc->current_position,
+ bcount);
else
- idefloppy_output_buffers(drive, pc, bcount.all);
+ idefloppy_output_buffers(drive, pc, bcount);
} else {
if (pc->buffer != NULL)
/* Read the current buffer */
- HWIF(drive)->atapi_input_bytes(drive,
- pc->current_position,
- bcount.all);
+ hwif->atapi_input_bytes(drive, pc->current_position,
+ bcount);
else
- idefloppy_input_buffers(drive, pc, bcount.all);
+ idefloppy_input_buffers(drive, pc, bcount);
}
/* Update the current position */
- pc->actually_transferred += bcount.all;
- pc->current_position += bcount.all;
+ pc->actually_transferred += bcount;
+ pc->current_position += bcount;
BUG_ON(HWGROUP(drive)->handler != NULL);
ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); /* And set the interrupt handler again */
@@ -943,15 +919,15 @@ static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive)
{
ide_startstop_t startstop;
idefloppy_floppy_t *floppy = drive->driver_data;
- atapi_ireason_t ireason;
+ u8 ireason;
if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
printk(KERN_ERR "ide-floppy: Strange, packet command "
"initiated yet DRQ isn't asserted\n");
return startstop;
}
- ireason.all = HWIF(drive)->INB(IDE_IREASON_REG);
- if (!ireason.b.cod || ireason.b.io) {
+ ireason = drive->hwif->INB(IDE_IREASON_REG);
+ if ((ireason & CD) == 0 || (ireason & IO)) {
printk(KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while "
"issuing a packet command\n");
return ide_do_reset(drive);
@@ -991,15 +967,15 @@ static ide_startstop_t idefloppy_transfer_pc1 (ide_drive_t *drive)
{
idefloppy_floppy_t *floppy = drive->driver_data;
ide_startstop_t startstop;
- atapi_ireason_t ireason;
+ u8 ireason;
if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
printk(KERN_ERR "ide-floppy: Strange, packet command "
"initiated yet DRQ isn't asserted\n");
return startstop;
}
- ireason.all = HWIF(drive)->INB(IDE_IREASON_REG);
- if (!ireason.b.cod || ireason.b.io) {
+ ireason = drive->hwif->INB(IDE_IREASON_REG);
+ if ((ireason & CD) == 0 || (ireason & IO)) {
printk(KERN_ERR "ide-floppy: (IO,CoD) != (0,1) "
"while issuing a packet command\n");
return ide_do_reset(drive);
@@ -1041,21 +1017,9 @@ static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *p
{
idefloppy_floppy_t *floppy = drive->driver_data;
ide_hwif_t *hwif = drive->hwif;
- atapi_feature_t feature;
- atapi_bcount_t bcount;
ide_handler_t *pkt_xfer_routine;
-
-#if 0 /* Accessing floppy->pc is not valid here, the previous pc may be gone
- and have lived on another thread's stack; that stack may have become
- unmapped meanwhile (CONFIG_DEBUG_PAGEALLOC). */
-#if IDEFLOPPY_DEBUG_BUGS
- if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD &&
- pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
- printk(KERN_ERR "ide-floppy: possible ide-floppy.c bug - "
- "Two request sense in serial were issued\n");
- }
-#endif /* IDEFLOPPY_DEBUG_BUGS */
-#endif
+ u16 bcount;
+ u8 dma;
if (floppy->failed_pc == NULL &&
pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD)
@@ -1093,25 +1057,20 @@ static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *p
/* We haven't transferred any data yet */
pc->actually_transferred = 0;
pc->current_position = pc->buffer;
- bcount.all = min(pc->request_transfer, 63 * 1024);
+ bcount = min(pc->request_transfer, 63 * 1024);
if (test_and_clear_bit(PC_DMA_ERROR, &pc->flags))
ide_dma_off(drive);
- feature.all = 0;
+ dma = 0;
if (test_bit(PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
- feature.b.dma = !hwif->dma_setup(drive);
+ dma = !hwif->dma_setup(drive);
- if (IDE_CONTROL_REG)
- HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG);
- /* Use PIO/DMA */
- HWIF(drive)->OUTB(feature.all, IDE_FEATURE_REG);
- HWIF(drive)->OUTB(bcount.b.high, IDE_BCOUNTH_REG);
- HWIF(drive)->OUTB(bcount.b.low, IDE_BCOUNTL_REG);
- HWIF(drive)->OUTB(drive->select.all, IDE_SELECT_REG);
+ ide_pktcmd_tf_load(drive, IDE_TFLAG_NO_SELECT_MASK |
+ IDE_TFLAG_OUT_DEVICE, bcount, dma);
- if (feature.b.dma) { /* Begin DMA, if necessary */
+ if (dma) { /* Begin DMA, if necessary */
set_bit(PC_DMA_IN_PROGRESS, &pc->flags);
hwif->dma_start(drive);
}
@@ -1665,14 +1624,14 @@ static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg)
/* Else assume format_unit has finished, and we're
** at 0x10000 */
} else {
- atapi_status_t status;
unsigned long flags;
+ u8 stat;
local_irq_save(flags);
- status.all = HWIF(drive)->INB(IDE_STATUS_REG);
+ stat = drive->hwif->INB(IDE_STATUS_REG);
local_irq_restore(flags);
- progress_indication = !status.b.dsc ? 0 : 0x10000;
+ progress_indication = ((stat & SEEK_STAT) == 0) ? 0 : 0x10000;
}
if (put_user(progress_indication, arg))
return (-EFAULT);
diff --git a/trunk/drivers/ide/ide-io.c b/trunk/drivers/ide/ide-io.c
index bef781fec500..2711b5a6962d 100644
--- a/trunk/drivers/ide/ide-io.c
+++ b/trunk/drivers/ide/ide-io.c
@@ -189,18 +189,14 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *
return ide_stopped;
}
if (ide_id_has_flush_cache_ext(drive->id))
- args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT;
+ args->tf.command = WIN_FLUSH_CACHE_EXT;
else
- args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE;
- args->command_type = IDE_DRIVE_TASK_NO_DATA;
- args->handler = &task_no_data_intr;
- return do_rw_taskfile(drive, args);
+ args->tf.command = WIN_FLUSH_CACHE;
+ goto out_do_tf;
case idedisk_pm_standby: /* Suspend step 2 (standby) */
- args->tfRegister[IDE_COMMAND_OFFSET] = WIN_STANDBYNOW1;
- args->command_type = IDE_DRIVE_TASK_NO_DATA;
- args->handler = &task_no_data_intr;
- return do_rw_taskfile(drive, args);
+ args->tf.command = WIN_STANDBYNOW1;
+ goto out_do_tf;
case idedisk_pm_restore_pio: /* Resume step 1 (restore PIO) */
ide_set_max_pio(drive);
@@ -214,10 +210,8 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *
return ide_stopped;
case idedisk_pm_idle: /* Resume step 2 (idle) */
- args->tfRegister[IDE_COMMAND_OFFSET] = WIN_IDLEIMMEDIATE;
- args->command_type = IDE_DRIVE_TASK_NO_DATA;
- args->handler = task_no_data_intr;
- return do_rw_taskfile(drive, args);
+ args->tf.command = WIN_IDLEIMMEDIATE;
+ goto out_do_tf;
case ide_pm_restore_dma: /* Resume step 3 (restore DMA) */
/*
@@ -227,7 +221,6 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *
*/
if (drive->hwif->ide_dma_on == NULL)
break;
- drive->hwif->dma_off_quietly(drive);
/*
* TODO: respect ->using_dma setting
*/
@@ -236,6 +229,11 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *
}
pm->pm_step = ide_pm_state_completed;
return ide_stopped;
+
+out_do_tf:
+ args->tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ args->data_phase = TASKFILE_NO_DATA;
+ return do_rw_taskfile(drive, args);
}
/**
@@ -298,6 +296,48 @@ static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq)
spin_unlock_irqrestore(&ide_lock, flags);
}
+void ide_tf_read(ide_drive_t *drive, ide_task_t *task)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_taskfile *tf = &task->tf;
+
+ if (task->tf_flags & IDE_TFLAG_IN_DATA) {
+ u16 data = hwif->INW(IDE_DATA_REG);
+
+ tf->data = data & 0xff;
+ tf->hob_data = (data >> 8) & 0xff;
+ }
+
+ /* be sure we're looking at the low order bits */
+ hwif->OUTB(drive->ctl & ~0x80, IDE_CONTROL_REG);
+
+ if (task->tf_flags & IDE_TFLAG_IN_NSECT)
+ tf->nsect = hwif->INB(IDE_NSECTOR_REG);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAL)
+ tf->lbal = hwif->INB(IDE_SECTOR_REG);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAM)
+ tf->lbam = hwif->INB(IDE_LCYL_REG);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAH)
+ tf->lbah = hwif->INB(IDE_HCYL_REG);
+ if (task->tf_flags & IDE_TFLAG_IN_DEVICE)
+ tf->device = hwif->INB(IDE_SELECT_REG);
+
+ if (task->tf_flags & IDE_TFLAG_LBA48) {
+ hwif->OUTB(drive->ctl | 0x80, IDE_CONTROL_REG);
+
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
+ tf->hob_feature = hwif->INB(IDE_FEATURE_REG);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_NSECT)
+ tf->hob_nsect = hwif->INB(IDE_NSECTOR_REG);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAL)
+ tf->hob_lbal = hwif->INB(IDE_SECTOR_REG);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAM)
+ tf->hob_lbam = hwif->INB(IDE_LCYL_REG);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAH)
+ tf->hob_lbah = hwif->INB(IDE_HCYL_REG);
+ }
+}
+
/**
* ide_end_drive_cmd - end an explicit drive command
* @drive: command
@@ -332,51 +372,22 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
args[1] = err;
args[2] = hwif->INB(IDE_NSECTOR_REG);
}
- } else if (rq->cmd_type == REQ_TYPE_ATA_TASK) {
- u8 *args = (u8 *) rq->buffer;
- if (rq->errors == 0)
- rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
-
- if (args) {
- args[0] = stat;
- args[1] = err;
- /* be sure we're looking at the low order bits */
- hwif->OUTB(drive->ctl & ~0x80, IDE_CONTROL_REG);
- args[2] = hwif->INB(IDE_NSECTOR_REG);
- args[3] = hwif->INB(IDE_SECTOR_REG);
- args[4] = hwif->INB(IDE_LCYL_REG);
- args[5] = hwif->INB(IDE_HCYL_REG);
- args[6] = hwif->INB(IDE_SELECT_REG);
- }
} else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *args = (ide_task_t *) rq->special;
if (rq->errors == 0)
rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
if (args) {
- if (args->tf_in_flags.b.data) {
- u16 data = hwif->INW(IDE_DATA_REG);
- args->tfRegister[IDE_DATA_OFFSET] = (data) & 0xFF;
- args->hobRegister[IDE_DATA_OFFSET] = (data >> 8) & 0xFF;
- }
- args->tfRegister[IDE_ERROR_OFFSET] = err;
- /* be sure we're looking at the low order bits */
- hwif->OUTB(drive->ctl & ~0x80, IDE_CONTROL_REG);
- args->tfRegister[IDE_NSECTOR_OFFSET] = hwif->INB(IDE_NSECTOR_REG);
- args->tfRegister[IDE_SECTOR_OFFSET] = hwif->INB(IDE_SECTOR_REG);
- args->tfRegister[IDE_LCYL_OFFSET] = hwif->INB(IDE_LCYL_REG);
- args->tfRegister[IDE_HCYL_OFFSET] = hwif->INB(IDE_HCYL_REG);
- args->tfRegister[IDE_SELECT_OFFSET] = hwif->INB(IDE_SELECT_REG);
- args->tfRegister[IDE_STATUS_OFFSET] = stat;
-
- if (drive->addressing == 1) {
- hwif->OUTB(drive->ctl|0x80, IDE_CONTROL_REG);
- args->hobRegister[IDE_FEATURE_OFFSET] = hwif->INB(IDE_FEATURE_REG);
- args->hobRegister[IDE_NSECTOR_OFFSET] = hwif->INB(IDE_NSECTOR_REG);
- args->hobRegister[IDE_SECTOR_OFFSET] = hwif->INB(IDE_SECTOR_REG);
- args->hobRegister[IDE_LCYL_OFFSET] = hwif->INB(IDE_LCYL_REG);
- args->hobRegister[IDE_HCYL_OFFSET] = hwif->INB(IDE_HCYL_REG);
- }
+ struct ide_taskfile *tf = &args->tf;
+
+ tf->error = err;
+ tf->status = stat;
+
+ args->tf_flags |= (IDE_TFLAG_IN_TF|IDE_TFLAG_IN_DEVICE);
+ if (args->tf_flags & IDE_TFLAG_LBA48)
+ args->tf_flags |= IDE_TFLAG_IN_HOB;
+
+ ide_tf_read(drive, args);
}
} else if (blk_pm_request(rq)) {
struct request_pm_state *pm = rq->data;
@@ -615,28 +626,6 @@ ide_startstop_t ide_abort(ide_drive_t *drive, const char *msg)
return __ide_abort(drive, rq);
}
-/**
- * ide_cmd - issue a simple drive command
- * @drive: drive the command is for
- * @cmd: command byte
- * @nsect: sector byte
- * @handler: handler for the command completion
- *
- * Issue a simple drive command with interrupts.
- * The drive must be selected beforehand.
- */
-
-static void ide_cmd (ide_drive_t *drive, u8 cmd, u8 nsect,
- ide_handler_t *handler)
-{
- ide_hwif_t *hwif = HWIF(drive);
- if (IDE_CONTROL_REG)
- hwif->OUTB(drive->ctl,IDE_CONTROL_REG); /* clear nIEN */
- SELECT_MASK(drive,0);
- hwif->OUTB(nsect,IDE_NSECTOR_REG);
- ide_execute_command(drive, cmd, handler, WAIT_CMD, NULL);
-}
-
/**
* drive_cmd_intr - drive command completion interrupt
* @drive: drive the completion interrupt occurred on
@@ -673,32 +662,26 @@ static ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
return ide_stopped;
}
-static void ide_init_specify_cmd(ide_drive_t *drive, ide_task_t *task)
+static void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf)
{
- task->tfRegister[IDE_NSECTOR_OFFSET] = drive->sect;
- task->tfRegister[IDE_SECTOR_OFFSET] = drive->sect;
- task->tfRegister[IDE_LCYL_OFFSET] = drive->cyl;
- task->tfRegister[IDE_HCYL_OFFSET] = drive->cyl>>8;
- task->tfRegister[IDE_SELECT_OFFSET] = ((drive->head-1)|drive->select.all)&0xBF;
- task->tfRegister[IDE_COMMAND_OFFSET] = WIN_SPECIFY;
-
- task->handler = &set_geometry_intr;
+ tf->nsect = drive->sect;
+ tf->lbal = drive->sect;
+ tf->lbam = drive->cyl;
+ tf->lbah = drive->cyl >> 8;
+ tf->device = ((drive->head - 1) | drive->select.all) & ~ATA_LBA;
+ tf->command = WIN_SPECIFY;
}
-static void ide_init_restore_cmd(ide_drive_t *drive, ide_task_t *task)
+static void ide_tf_set_restore_cmd(ide_drive_t *drive, struct ide_taskfile *tf)
{
- task->tfRegister[IDE_NSECTOR_OFFSET] = drive->sect;
- task->tfRegister[IDE_COMMAND_OFFSET] = WIN_RESTORE;
-
- task->handler = &recal_intr;
+ tf->nsect = drive->sect;
+ tf->command = WIN_RESTORE;
}
-static void ide_init_setmult_cmd(ide_drive_t *drive, ide_task_t *task)
+static void ide_tf_set_setmult_cmd(ide_drive_t *drive, struct ide_taskfile *tf)
{
- task->tfRegister[IDE_NSECTOR_OFFSET] = drive->mult_req;
- task->tfRegister[IDE_COMMAND_OFFSET] = WIN_SETMULT;
-
- task->handler = &set_multmode_intr;
+ tf->nsect = drive->mult_req;
+ tf->command = WIN_SETMULT;
}
static ide_startstop_t ide_disk_special(ide_drive_t *drive)
@@ -707,19 +690,19 @@ static ide_startstop_t ide_disk_special(ide_drive_t *drive)
ide_task_t args;
memset(&args, 0, sizeof(ide_task_t));
- args.command_type = IDE_DRIVE_TASK_NO_DATA;
+ args.data_phase = TASKFILE_NO_DATA;
if (s->b.set_geometry) {
s->b.set_geometry = 0;
- ide_init_specify_cmd(drive, &args);
+ ide_tf_set_specify_cmd(drive, &args.tf);
} else if (s->b.recalibrate) {
s->b.recalibrate = 0;
- ide_init_restore_cmd(drive, &args);
+ ide_tf_set_restore_cmd(drive, &args.tf);
} else if (s->b.set_multmode) {
s->b.set_multmode = 0;
if (drive->mult_req > drive->id->max_multsect)
drive->mult_req = drive->id->max_multsect;
- ide_init_setmult_cmd(drive, &args);
+ ide_tf_set_setmult_cmd(drive, &args.tf);
} else if (s->all) {
int special = s->all;
s->all = 0;
@@ -727,6 +710,9 @@ static ide_startstop_t ide_disk_special(ide_drive_t *drive)
return ide_stopped;
}
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE |
+ IDE_TFLAG_CUSTOM_HANDLER;
+
do_rw_taskfile(drive, &args);
return ide_started;
@@ -861,13 +847,17 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive,
struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
+ u8 *args = rq->buffer;
+ ide_task_t ltask;
+ struct ide_taskfile *tf = <ask.tf;
+
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
- ide_task_t *args = rq->special;
+ ide_task_t *task = rq->special;
- if (!args)
+ if (task == NULL)
goto done;
- hwif->data_phase = args->data_phase;
+ hwif->data_phase = task->data_phase;
switch (hwif->data_phase) {
case TASKFILE_MULTI_OUT:
@@ -880,55 +870,34 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive,
break;
}
- if (args->tf_out_flags.all != 0)
- return flagged_taskfile(drive, args);
- return do_rw_taskfile(drive, args);
- } else if (rq->cmd_type == REQ_TYPE_ATA_TASK) {
- u8 *args = rq->buffer;
-
- if (!args)
- goto done;
-#ifdef DEBUG
- printk("%s: DRIVE_TASK_CMD ", drive->name);
- printk("cmd=0x%02x ", args[0]);
- printk("fr=0x%02x ", args[1]);
- printk("ns=0x%02x ", args[2]);
- printk("sc=0x%02x ", args[3]);
- printk("lcyl=0x%02x ", args[4]);
- printk("hcyl=0x%02x ", args[5]);
- printk("sel=0x%02x\n", args[6]);
-#endif
- hwif->OUTB(args[1], IDE_FEATURE_REG);
- hwif->OUTB(args[3], IDE_SECTOR_REG);
- hwif->OUTB(args[4], IDE_LCYL_REG);
- hwif->OUTB(args[5], IDE_HCYL_REG);
- hwif->OUTB((args[6] & 0xEF)|drive->select.all, IDE_SELECT_REG);
- ide_cmd(drive, args[0], args[2], &drive_cmd_intr);
- return ide_started;
- } else if (rq->cmd_type == REQ_TYPE_ATA_CMD) {
- u8 *args = rq->buffer;
-
- if (!args)
- goto done;
+ return do_rw_taskfile(drive, task);
+ }
+
+ if (args == NULL)
+ goto done;
+
+ memset(<ask, 0, sizeof(ltask));
+ if (rq->cmd_type == REQ_TYPE_ATA_CMD) {
#ifdef DEBUG
- printk("%s: DRIVE_CMD ", drive->name);
- printk("cmd=0x%02x ", args[0]);
- printk("sc=0x%02x ", args[1]);
- printk("fr=0x%02x ", args[2]);
- printk("xx=0x%02x\n", args[3]);
+ printk("%s: DRIVE_CMD\n", drive->name);
#endif
- if (args[0] == WIN_SMART) {
- hwif->OUTB(0x4f, IDE_LCYL_REG);
- hwif->OUTB(0xc2, IDE_HCYL_REG);
- hwif->OUTB(args[2],IDE_FEATURE_REG);
- hwif->OUTB(args[1],IDE_SECTOR_REG);
- ide_cmd(drive, args[0], args[3], &drive_cmd_intr);
- return ide_started;
- }
- hwif->OUTB(args[2],IDE_FEATURE_REG);
- ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
- return ide_started;
+ tf->feature = args[2];
+ if (args[0] == WIN_SMART) {
+ tf->nsect = args[3];
+ tf->lbal = args[1];
+ tf->lbam = 0x4f;
+ tf->lbah = 0xc2;
+ ltask.tf_flags = IDE_TFLAG_OUT_TF;
+ } else {
+ tf->nsect = args[1];
+ ltask.tf_flags = IDE_TFLAG_OUT_FEATURE |
+ IDE_TFLAG_OUT_NSECT;
+ }
}
+ tf->command = args[0];
+ ide_tf_load(drive, <ask);
+ ide_execute_command(drive, args[0], &drive_cmd_intr, WAIT_WORSTCASE, NULL);
+ return ide_started;
done:
/*
@@ -1003,6 +972,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
/* bail early if we've exceeded max_failures */
if (drive->max_failures && (drive->failures > drive->max_failures)) {
+ rq->cmd_flags |= REQ_FAILED;
goto kill_rq;
}
@@ -1035,7 +1005,6 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
ide_config_drive_speed(drive, drive->desired_speed);
if (rq->cmd_type == REQ_TYPE_ATA_CMD ||
- rq->cmd_type == REQ_TYPE_ATA_TASK ||
rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
return execute_drive_cmd(drive, rq);
else if (blk_pm_request(rq)) {
@@ -1247,8 +1216,12 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
if (hwgroup->hwif->sharing_irq &&
hwif != hwgroup->hwif &&
hwif->io_ports[IDE_CONTROL_OFFSET]) {
- /* set nIEN for previous hwif */
- SELECT_INTERRUPT(drive);
+ /*
+ * set nIEN for previous hwif, drives in the
+ * quirk_list may not like intr setups/cleanups
+ */
+ if (drive->quirk_list != 1)
+ hwif->OUTB(drive->ctl | 2, IDE_CONTROL_REG);
}
hwgroup->hwif = hwif;
hwgroup->drive = drive;
@@ -1454,12 +1427,8 @@ void ide_timer_expiry (unsigned long data)
*/
spin_unlock(&ide_lock);
hwif = HWIF(drive);
-#if DISABLE_IRQ_NOSYNC
- disable_irq_nosync(hwif->irq);
-#else
/* disable_irq_nosync ?? */
disable_irq(hwif->irq);
-#endif /* DISABLE_IRQ_NOSYNC */
/* local CPU only,
* as if we were handling an interrupt */
local_irq_disable();
@@ -1785,3 +1754,19 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
}
EXPORT_SYMBOL(ide_do_drive_cmd);
+
+void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma)
+{
+ ide_task_t task;
+
+ memset(&task, 0, sizeof(task));
+ task.tf_flags = IDE_TFLAG_OUT_LBAH | IDE_TFLAG_OUT_LBAM |
+ IDE_TFLAG_OUT_FEATURE | tf_flags;
+ task.tf.feature = dma; /* Use PIO/DMA */
+ task.tf.lbam = bcount & 0xff;
+ task.tf.lbah = (bcount >> 8) & 0xff;
+
+ ide_tf_load(drive, &task);
+}
+
+EXPORT_SYMBOL_GPL(ide_pktcmd_tf_load);
diff --git a/trunk/drivers/ide/ide-iops.c b/trunk/drivers/ide/ide-iops.c
index bb9693dabe41..c97c0719ddf1 100644
--- a/trunk/drivers/ide/ide-iops.c
+++ b/trunk/drivers/ide/ide-iops.c
@@ -158,14 +158,6 @@ void default_hwif_mmiops (ide_hwif_t *hwif)
EXPORT_SYMBOL(default_hwif_mmiops);
-u32 ide_read_24 (ide_drive_t *drive)
-{
- u8 hcyl = HWIF(drive)->INB(IDE_HCYL_REG);
- u8 lcyl = HWIF(drive)->INB(IDE_LCYL_REG);
- u8 sect = HWIF(drive)->INB(IDE_SECTOR_REG);
- return (hcyl<<16)|(lcyl<<8)|sect;
-}
-
void SELECT_DRIVE (ide_drive_t *drive)
{
if (HWIF(drive)->selectproc)
@@ -175,26 +167,12 @@ void SELECT_DRIVE (ide_drive_t *drive)
EXPORT_SYMBOL(SELECT_DRIVE);
-void SELECT_INTERRUPT (ide_drive_t *drive)
-{
- if (HWIF(drive)->intrproc)
- HWIF(drive)->intrproc(drive);
- else
- HWIF(drive)->OUTB(drive->ctl|2, IDE_CONTROL_REG);
-}
-
void SELECT_MASK (ide_drive_t *drive, int mask)
{
if (HWIF(drive)->maskproc)
HWIF(drive)->maskproc(drive, mask);
}
-void QUIRK_LIST (ide_drive_t *drive)
-{
- if (HWIF(drive)->quirkproc)
- drive->quirk_list = HWIF(drive)->quirkproc(drive);
-}
-
/*
* Some localbus EIDE interfaces require a special access sequence
* when using 32-bit I/O instructions to transfer data. We call this
@@ -449,7 +427,6 @@ int drive_is_ready (ide_drive_t *drive)
udelay(1);
#endif
-#ifdef CONFIG_IDEPCI_SHARE_IRQ
/*
* We do a passive status test under shared PCI interrupts on
* cards that truly share the ATA side interrupt, but may also share
@@ -459,7 +436,6 @@ int drive_is_ready (ide_drive_t *drive)
if (IDE_CONTROL_REG)
stat = hwif->INB(IDE_ALTSTATUS_REG);
else
-#endif /* CONFIG_IDEPCI_SHARE_IRQ */
/* Note: this may clear a pending IRQ!! */
stat = hwif->INB(IDE_STATUS_REG);
@@ -642,9 +618,9 @@ u8 eighty_ninty_three (ide_drive_t *drive)
int ide_ata66_check (ide_drive_t *drive, ide_task_t *args)
{
- if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) &&
- (args->tfRegister[IDE_SECTOR_OFFSET] > XFER_UDMA_2) &&
- (args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER)) {
+ if (args->tf.command == WIN_SETFEATURES &&
+ args->tf.lbal > XFER_UDMA_2 &&
+ args->tf.feature == SETFEATURES_XFER) {
if (eighty_ninty_three(drive) == 0) {
printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot "
"be set\n", drive->name);
@@ -662,9 +638,9 @@ int ide_ata66_check (ide_drive_t *drive, ide_task_t *args)
*/
int set_transfer (ide_drive_t *drive, ide_task_t *args)
{
- if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) &&
- (args->tfRegister[IDE_SECTOR_OFFSET] >= XFER_SW_DMA_0) &&
- (args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER) &&
+ if (args->tf.command == WIN_SETFEATURES &&
+ args->tf.lbal >= XFER_SW_DMA_0 &&
+ args->tf.feature == SETFEATURES_XFER &&
(drive->id->dma_ultra ||
drive->id->dma_mword ||
drive->id->dma_1word))
@@ -902,8 +878,9 @@ EXPORT_SYMBOL(ide_set_handler);
* handler and IRQ setup do not race. All IDE command kick off
* should go via this function or do equivalent locking.
*/
-
-void ide_execute_command(ide_drive_t *drive, task_ioreg_t cmd, ide_handler_t *handler, unsigned timeout, ide_expiry_t *expiry)
+
+void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler,
+ unsigned timeout, ide_expiry_t *expiry)
{
unsigned long flags;
ide_hwgroup_t *hwgroup = HWGROUP(drive);
@@ -1051,8 +1028,7 @@ static void ide_disk_pre_reset(ide_drive_t *drive)
drive->special.all = 0;
drive->special.b.set_geometry = legacy;
drive->special.b.recalibrate = legacy;
- if (OK_TO_RESET_CONTROLLER)
- drive->mult_count = 0;
+ drive->mult_count = 0;
if (!drive->keep_settings && !drive->using_dma)
drive->mult_req = 0;
if (drive->mult_req != drive->mult_count)
@@ -1137,7 +1113,6 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
for (unit = 0; unit < MAX_DRIVES; ++unit)
pre_reset(&hwif->drives[unit]);
-#if OK_TO_RESET_CONTROLLER
if (!IDE_CONTROL_REG) {
spin_unlock_irqrestore(&ide_lock, flags);
return ide_stopped;
@@ -1174,11 +1149,8 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
* state when the disks are reset this way. At least, the Winbond
* 553 documentation says that
*/
- if (hwif->resetproc != NULL) {
+ if (hwif->resetproc)
hwif->resetproc(drive);
- }
-
-#endif /* OK_TO_RESET_CONTROLLER */
spin_unlock_irqrestore(&ide_lock, flags);
return ide_started;
diff --git a/trunk/drivers/ide/ide-lib.c b/trunk/drivers/ide/ide-lib.c
index 062d3bcb2471..a3bd8e8ed6b0 100644
--- a/trunk/drivers/ide/ide-lib.c
+++ b/trunk/drivers/ide/ide-lib.c
@@ -441,6 +441,12 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
* case could happen iff the transfer mode has already been set on
* the device by ide-proc.c::set_xfer_rate()).
*/
+ if (rate < XFER_PIO_0) {
+ if (hwif->host_flags & IDE_HFLAG_ABUSE_SET_DMA_MODE)
+ return ide_set_dma_mode(drive, rate);
+ else
+ return ide_config_drive_speed(drive, rate);
+ }
return ide_set_dma_mode(drive, rate);
}
@@ -458,8 +464,7 @@ static void ide_dump_opcode(ide_drive_t *drive)
spin_unlock(&ide_lock);
if (!rq)
return;
- if (rq->cmd_type == REQ_TYPE_ATA_CMD ||
- rq->cmd_type == REQ_TYPE_ATA_TASK) {
+ if (rq->cmd_type == REQ_TYPE_ATA_CMD) {
char *args = rq->buffer;
if (args) {
opcode = args[0];
@@ -468,8 +473,7 @@ static void ide_dump_opcode(ide_drive_t *drive)
} else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *args = rq->special;
if (args) {
- task_struct_t *tf = (task_struct_t *) args->tfRegister;
- opcode = tf->command;
+ opcode = args->tf.command;
found = 1;
}
}
@@ -481,141 +485,118 @@ static void ide_dump_opcode(ide_drive_t *drive)
printk("0x%02x\n", opcode);
}
-static u8 ide_dump_ata_status(ide_drive_t *drive, const char *msg, u8 stat)
+u64 ide_get_lba_addr(struct ide_taskfile *tf, int lba48)
{
- ide_hwif_t *hwif = HWIF(drive);
- unsigned long flags;
- u8 err = 0;
+ u32 high, low;
- local_irq_save(flags);
- printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
- if (stat & BUSY_STAT)
- printk("Busy ");
- else {
- if (stat & READY_STAT) printk("DriveReady ");
- if (stat & WRERR_STAT) printk("DeviceFault ");
- if (stat & SEEK_STAT) printk("SeekComplete ");
- if (stat & DRQ_STAT) printk("DataRequest ");
- if (stat & ECC_STAT) printk("CorrectedError ");
- if (stat & INDEX_STAT) printk("Index ");
- if (stat & ERR_STAT) printk("Error ");
+ if (lba48)
+ high = (tf->hob_lbah << 16) | (tf->hob_lbam << 8) |
+ tf->hob_lbal;
+ else
+ high = tf->device & 0xf;
+ low = (tf->lbah << 16) | (tf->lbam << 8) | tf->lbal;
+
+ return ((u64)high << 24) | low;
+}
+EXPORT_SYMBOL_GPL(ide_get_lba_addr);
+
+static void ide_dump_sector(ide_drive_t *drive)
+{
+ ide_task_t task;
+ struct ide_taskfile *tf = &task.tf;
+ int lba48 = (drive->addressing == 1) ? 1 : 0;
+
+ memset(&task, 0, sizeof(task));
+ if (lba48)
+ task.tf_flags = IDE_TFLAG_IN_LBA | IDE_TFLAG_IN_HOB_LBA |
+ IDE_TFLAG_LBA48;
+ else
+ task.tf_flags = IDE_TFLAG_IN_LBA | IDE_TFLAG_IN_DEVICE;
+
+ ide_tf_read(drive, &task);
+
+ if (lba48 || (tf->device & ATA_LBA))
+ printk(", LBAsect=%llu",
+ (unsigned long long)ide_get_lba_addr(tf, lba48));
+ else
+ printk(", CHS=%d/%d/%d", (tf->lbah << 8) + tf->lbam,
+ tf->device & 0xf, tf->lbal);
+}
+
+static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
+{
+ printk("{ ");
+ if (err & ABRT_ERR) printk("DriveStatusError ");
+ if (err & ICRC_ERR)
+ printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
+ if (err & ECC_ERR) printk("UncorrectableError ");
+ if (err & ID_ERR) printk("SectorIdNotFound ");
+ if (err & TRK0_ERR) printk("TrackZeroNotFound ");
+ if (err & MARK_ERR) printk("AddrMarkNotFound ");
+ printk("}");
+ if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR ||
+ (err & (ECC_ERR|ID_ERR|MARK_ERR))) {
+ ide_dump_sector(drive);
+ if (HWGROUP(drive) && HWGROUP(drive)->rq)
+ printk(", sector=%llu",
+ (unsigned long long)HWGROUP(drive)->rq->sector);
}
+ printk("\n");
+}
+
+static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
+{
+ printk("{ ");
+ if (err & ILI_ERR) printk("IllegalLengthIndication ");
+ if (err & EOM_ERR) printk("EndOfMedia ");
+ if (err & ABRT_ERR) printk("AbortedCommand ");
+ if (err & MCR_ERR) printk("MediaChangeRequested ");
+ if (err & LFS_ERR) printk("LastFailedSense=0x%02x ",
+ (err & LFS_ERR) >> 4);
printk("}\n");
- if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
- err = hwif->INB(IDE_ERROR_REG);
- printk("%s: %s: error=0x%02x { ", drive->name, msg, err);
- if (err & ABRT_ERR) printk("DriveStatusError ");
- if (err & ICRC_ERR)
- printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
- if (err & ECC_ERR) printk("UncorrectableError ");
- if (err & ID_ERR) printk("SectorIdNotFound ");
- if (err & TRK0_ERR) printk("TrackZeroNotFound ");
- if (err & MARK_ERR) printk("AddrMarkNotFound ");
- printk("}");
- if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR ||
- (err & (ECC_ERR|ID_ERR|MARK_ERR))) {
- if (drive->addressing == 1) {
- __u64 sectors = 0;
- u32 low = 0, high = 0;
- hwif->OUTB(drive->ctl&~0x80, IDE_CONTROL_REG);
- low = ide_read_24(drive);
- hwif->OUTB(drive->ctl|0x80, IDE_CONTROL_REG);
- high = ide_read_24(drive);
- sectors = ((__u64)high << 24) | low;
- printk(", LBAsect=%llu, high=%d, low=%d",
- (unsigned long long) sectors,
- high, low);
- } else {
- u8 cur = hwif->INB(IDE_SELECT_REG);
- if (cur & 0x40) { /* using LBA? */
- printk(", LBAsect=%ld", (unsigned long)
- ((cur&0xf)<<24)
- |(hwif->INB(IDE_HCYL_REG)<<16)
- |(hwif->INB(IDE_LCYL_REG)<<8)
- | hwif->INB(IDE_SECTOR_REG));
- } else {
- printk(", CHS=%d/%d/%d",
- (hwif->INB(IDE_HCYL_REG)<<8) +
- hwif->INB(IDE_LCYL_REG),
- cur & 0xf,
- hwif->INB(IDE_SECTOR_REG));
- }
- }
- if (HWGROUP(drive) && HWGROUP(drive)->rq)
- printk(", sector=%llu",
- (unsigned long long)HWGROUP(drive)->rq->sector);
- }
- printk("\n");
- }
- ide_dump_opcode(drive);
- local_irq_restore(flags);
- return err;
}
/**
- * ide_dump_atapi_status - print human readable atapi status
+ * ide_dump_status - translate ATA/ATAPI error
* @drive: drive that status applies to
* @msg: text message to print
* @stat: status byte to decode
*
* Error reporting, in human readable form (luxurious, but a memory hog).
+ * Combines the drive name, message and status byte to provide a
+ * user understandable explanation of the device error.
*/
-static u8 ide_dump_atapi_status(ide_drive_t *drive, const char *msg, u8 stat)
+u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
{
unsigned long flags;
+ u8 err = 0;
- atapi_status_t status;
- atapi_error_t error;
-
- status.all = stat;
- error.all = 0;
local_irq_save(flags);
printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
- if (status.b.bsy)
+ if (stat & BUSY_STAT)
printk("Busy ");
else {
- if (status.b.drdy) printk("DriveReady ");
- if (status.b.df) printk("DeviceFault ");
- if (status.b.dsc) printk("SeekComplete ");
- if (status.b.drq) printk("DataRequest ");
- if (status.b.corr) printk("CorrectedError ");
- if (status.b.idx) printk("Index ");
- if (status.b.check) printk("Error ");
+ if (stat & READY_STAT) printk("DriveReady ");
+ if (stat & WRERR_STAT) printk("DeviceFault ");
+ if (stat & SEEK_STAT) printk("SeekComplete ");
+ if (stat & DRQ_STAT) printk("DataRequest ");
+ if (stat & ECC_STAT) printk("CorrectedError ");
+ if (stat & INDEX_STAT) printk("Index ");
+ if (stat & ERR_STAT) printk("Error ");
}
printk("}\n");
- if (status.b.check && !status.b.bsy) {
- error.all = HWIF(drive)->INB(IDE_ERROR_REG);
- printk("%s: %s: error=0x%02x { ", drive->name, msg, error.all);
- if (error.b.ili) printk("IllegalLengthIndication ");
- if (error.b.eom) printk("EndOfMedia ");
- if (error.b.abrt) printk("AbortedCommand ");
- if (error.b.mcr) printk("MediaChangeRequested ");
- if (error.b.sense_key) printk("LastFailedSense=0x%02x ",
- error.b.sense_key);
- printk("}\n");
+ if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
+ err = drive->hwif->INB(IDE_ERROR_REG);
+ printk("%s: %s: error=0x%02x ", drive->name, msg, err);
+ if (drive->media == ide_disk)
+ ide_dump_ata_error(drive, err);
+ else
+ ide_dump_atapi_error(drive, err);
}
ide_dump_opcode(drive);
local_irq_restore(flags);
- return error.all;
-}
-
-/**
- * ide_dump_status - translate ATA/ATAPI error
- * @drive: drive the error occured on
- * @msg: information string
- * @stat: status byte
- *
- * Error reporting, in human readable form (luxurious, but a memory hog).
- * Combines the drive name, message and status byte to provide a
- * user understandable explanation of the device error.
- */
-
-u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
-{
- if (drive->media == ide_disk)
- return ide_dump_ata_status(drive, msg, stat);
- return ide_dump_atapi_status(drive, msg, stat);
+ return err;
}
EXPORT_SYMBOL(ide_dump_status);
diff --git a/trunk/drivers/ide/ide-probe.c b/trunk/drivers/ide/ide-probe.c
index 0cb3d2bb3ab9..0379d1f697cf 100644
--- a/trunk/drivers/ide/ide-probe.c
+++ b/trunk/drivers/ide/ide-probe.c
@@ -95,10 +95,10 @@ static void ide_disk_init_mult_count(ide_drive_t *drive)
#ifdef CONFIG_IDEDISK_MULTI_MODE
id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
id->multsect_valid = id->multsect ? 1 : 0;
- drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT;
+ drive->mult_req = id->multsect_valid ? id->max_multsect : 0;
drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
#else /* original, pre IDE-NFG, per request of AC */
- drive->mult_req = INITIAL_MULT_COUNT;
+ drive->mult_req = 0;
if (drive->mult_req > id->max_multsect)
drive->mult_req = id->max_multsect;
if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
@@ -234,7 +234,10 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)
drive->media = ide_disk;
printk("%s DISK drive\n", (id->config == 0x848a) ? "CFA" : "ATA" );
- QUIRK_LIST(drive);
+
+ if (hwif->quirkproc)
+ drive->quirk_list = hwif->quirkproc(drive);
+
return;
err_misc:
@@ -830,16 +833,8 @@ static void probe_hwif(ide_hwif_t *hwif)
drive->nice1 = 1;
- if (hwif->ide_dma_on) {
- /*
- * Force DMAing for the beginning of the check.
- * Some chipsets appear to do interesting
- * things, if not checked and cleared.
- * PARANOIA!!!
- */
- hwif->dma_off_quietly(drive);
+ if (hwif->ide_dma_on)
ide_set_dma(drive);
- }
}
}
@@ -968,11 +963,6 @@ static int ide_init_queue(ide_drive_t *drive)
* Much of the code is for correctly detecting/handling irq sharing
* and irq serialization situations. This is somewhat complex because
* it handles static as well as dynamic (PCMCIA) IDE interfaces.
- *
- * The IRQF_DISABLED in sa_flags means ide_intr() is always entered with
- * interrupts completely disabled. This can be bad for interrupt latency,
- * but anything else has led to problems on some machines. We re-enable
- * interrupts as much as we can safely do in most places.
*/
static int init_irq (ide_hwif_t *hwif)
{
@@ -1055,17 +1045,13 @@ static int init_irq (ide_hwif_t *hwif)
* Allocate the irq, if not already obtained for another hwif
*/
if (!match || match->irq != hwif->irq) {
- int sa = IRQF_DISABLED;
+ int sa = 0;
#if defined(__mc68000__) || defined(CONFIG_APUS)
sa = IRQF_SHARED;
#endif /* __mc68000__ || CONFIG_APUS */
- if (IDE_CHIPSET_IS_PCI(hwif->chipset)) {
+ if (IDE_CHIPSET_IS_PCI(hwif->chipset))
sa = IRQF_SHARED;
-#ifndef CONFIG_IDEPCI_SHARE_IRQ
- sa |= IRQF_DISABLED;
-#endif /* CONFIG_IDEPCI_SHARE_IRQ */
- }
if (hwif->io_ports[IDE_CONTROL_OFFSET])
/* clear nIEN */
diff --git a/trunk/drivers/ide/ide-tape.c b/trunk/drivers/ide/ide-tape.c
index 1495792d7917..3cbca3f4628a 100644
--- a/trunk/drivers/ide/ide-tape.c
+++ b/trunk/drivers/ide/ide-tape.c
@@ -614,16 +614,6 @@ typedef struct os_dat_s {
/*************************** End of tunable parameters ***********************/
-/*
- * Debugging/Performance analysis
- *
- * I/O trace support
- */
-#define USE_IOTRACE 0
-#if USE_IOTRACE
-#define IO_IDETAPE_FIFO 500
-#endif
-
/*
* Read/Write error simulation
*/
@@ -1818,9 +1808,8 @@ static ide_startstop_t idetape_retry_pc (ide_drive_t *drive)
idetape_tape_t *tape = drive->driver_data;
idetape_pc_t *pc;
struct request *rq;
- atapi_error_t error;
- error.all = HWIF(drive)->INB(IDE_ERROR_REG);
+ (void)drive->hwif->INB(IDE_ERROR_REG);
pc = idetape_next_pc_storage(drive);
rq = idetape_next_rq_storage(drive);
idetape_create_request_sense_cmd(pc);
@@ -1858,15 +1847,13 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
idetape_tape_t *tape = drive->driver_data;
- atapi_status_t status;
- atapi_bcount_t bcount;
- atapi_ireason_t ireason;
idetape_pc_t *pc = tape->pc;
-
unsigned int temp;
#if SIMULATE_ERRORS
static int error_sim_count = 0;
#endif
+ u16 bcount;
+ u8 stat, ireason;
#if IDETAPE_DEBUG_LOG
if (tape->debug_level >= 4)
@@ -1875,10 +1862,10 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
#endif /* IDETAPE_DEBUG_LOG */
/* Clear the interrupt */
- status.all = HWIF(drive)->INB(IDE_STATUS_REG);
+ stat = hwif->INB(IDE_STATUS_REG);
if (test_bit(PC_DMA_IN_PROGRESS, &pc->flags)) {
- if (HWIF(drive)->ide_dma_end(drive) || status.b.check) {
+ if (hwif->ide_dma_end(drive) || (stat & ERR_STAT)) {
/*
* A DMA error is sometimes expected. For example,
* if the tape is crossing a filemark during a
@@ -1912,7 +1899,7 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
}
/* No more interrupts */
- if (!status.b.drq) {
+ if ((stat & DRQ_STAT) == 0) {
#if IDETAPE_DEBUG_LOG
if (tape->debug_level >= 2)
printk(KERN_INFO "ide-tape: Packet command completed, %d bytes transferred\n", pc->actually_transferred);
@@ -1927,12 +1914,13 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
(++error_sim_count % 100) == 0) {
printk(KERN_INFO "ide-tape: %s: simulating error\n",
tape->name);
- status.b.check = 1;
+ stat |= ERR_STAT;
}
#endif
- if (status.b.check && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD)
- status.b.check = 0;
- if (status.b.check || test_bit(PC_DMA_ERROR, &pc->flags)) { /* Error detected */
+ if ((stat & ERR_STAT) && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD)
+ stat &= ~ERR_STAT;
+ if ((stat & ERR_STAT) || test_bit(PC_DMA_ERROR, &pc->flags)) {
+ /* Error detected */
#if IDETAPE_DEBUG_LOG
if (tape->debug_level >= 1)
printk(KERN_INFO "ide-tape: %s: I/O error\n",
@@ -1951,7 +1939,7 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
}
pc->error = 0;
if (test_bit(PC_WAIT_FOR_DSC, &pc->flags) &&
- !status.b.dsc) {
+ (stat & SEEK_STAT) == 0) {
/* Media access command */
tape->dsc_polling_start = jiffies;
tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST;
@@ -1973,30 +1961,30 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
return ide_do_reset(drive);
}
/* Get the number of bytes to transfer on this interrupt. */
- bcount.b.high = hwif->INB(IDE_BCOUNTH_REG);
- bcount.b.low = hwif->INB(IDE_BCOUNTL_REG);
+ bcount = (hwif->INB(IDE_BCOUNTH_REG) << 8) |
+ hwif->INB(IDE_BCOUNTL_REG);
- ireason.all = hwif->INB(IDE_IREASON_REG);
+ ireason = hwif->INB(IDE_IREASON_REG);
- if (ireason.b.cod) {
+ if (ireason & CD) {
printk(KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n");
return ide_do_reset(drive);
}
- if (ireason.b.io == test_bit(PC_WRITING, &pc->flags)) {
+ if (((ireason & IO) == IO) == test_bit(PC_WRITING, &pc->flags)) {
/* Hopefully, we will never get here */
printk(KERN_ERR "ide-tape: We wanted to %s, ",
- ireason.b.io ? "Write":"Read");
+ (ireason & IO) ? "Write" : "Read");
printk(KERN_ERR "ide-tape: but the tape wants us to %s !\n",
- ireason.b.io ? "Read":"Write");
+ (ireason & IO) ? "Read" : "Write");
return ide_do_reset(drive);
}
if (!test_bit(PC_WRITING, &pc->flags)) {
/* Reading - Check that we have enough space */
- temp = pc->actually_transferred + bcount.all;
+ temp = pc->actually_transferred + bcount;
if (temp > pc->request_transfer) {
if (temp > pc->buffer_size) {
printk(KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n");
- idetape_discard_data(drive, bcount.all);
+ idetape_discard_data(drive, bcount);
ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL);
return ide_started;
}
@@ -2008,23 +1996,26 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
}
if (test_bit(PC_WRITING, &pc->flags)) {
if (pc->bh != NULL)
- idetape_output_buffers(drive, pc, bcount.all);
+ idetape_output_buffers(drive, pc, bcount);
else
/* Write the current buffer */
- HWIF(drive)->atapi_output_bytes(drive, pc->current_position, bcount.all);
+ hwif->atapi_output_bytes(drive, pc->current_position,
+ bcount);
} else {
if (pc->bh != NULL)
- idetape_input_buffers(drive, pc, bcount.all);
+ idetape_input_buffers(drive, pc, bcount);
else
/* Read the current buffer */
- HWIF(drive)->atapi_input_bytes(drive, pc->current_position, bcount.all);
+ hwif->atapi_input_bytes(drive, pc->current_position,
+ bcount);
}
/* Update the current position */
- pc->actually_transferred += bcount.all;
- pc->current_position += bcount.all;
+ pc->actually_transferred += bcount;
+ pc->current_position += bcount;
#if IDETAPE_DEBUG_LOG
if (tape->debug_level >= 2)
- printk(KERN_INFO "ide-tape: [cmd %x] transferred %d bytes on that interrupt\n", pc->c[0], bcount.all);
+ printk(KERN_INFO "ide-tape: [cmd %x] transferred %d bytes "
+ "on that interrupt\n", pc->c[0], bcount);
#endif
/* And set the interrupt handler again */
ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL);
@@ -2078,28 +2069,28 @@ static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
idetape_tape_t *tape = drive->driver_data;
idetape_pc_t *pc = tape->pc;
- atapi_ireason_t ireason;
int retries = 100;
ide_startstop_t startstop;
+ u8 ireason;
if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
printk(KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
return startstop;
}
- ireason.all = hwif->INB(IDE_IREASON_REG);
- while (retries-- && (!ireason.b.cod || ireason.b.io)) {
+ ireason = hwif->INB(IDE_IREASON_REG);
+ while (retries-- && ((ireason & CD) == 0 || (ireason & IO))) {
printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing "
"a packet command, retrying\n");
udelay(100);
- ireason.all = hwif->INB(IDE_IREASON_REG);
+ ireason = hwif->INB(IDE_IREASON_REG);
if (retries == 0) {
printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while "
"issuing a packet command, ignoring\n");
- ireason.b.cod = 1;
- ireason.b.io = 0;
+ ireason |= CD;
+ ireason &= ~IO;
}
}
- if (!ireason.b.cod || ireason.b.io) {
+ if ((ireason & CD) == 0 || (ireason & IO)) {
printk(KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing "
"a packet command\n");
return ide_do_reset(drive);
@@ -2120,8 +2111,8 @@ static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape
{
ide_hwif_t *hwif = drive->hwif;
idetape_tape_t *tape = drive->driver_data;
- atapi_bcount_t bcount;
int dma_ok = 0;
+ u16 bcount;
#if IDETAPE_DEBUG_BUGS
if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD &&
@@ -2170,7 +2161,7 @@ static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape
pc->actually_transferred = 0;
pc->current_position = pc->buffer;
/* Request to transfer the entire buffer at once */
- bcount.all = pc->request_transfer;
+ bcount = pc->request_transfer;
if (test_and_clear_bit(PC_DMA_ERROR, &pc->flags)) {
printk(KERN_WARNING "ide-tape: DMA disabled, "
@@ -2180,12 +2171,9 @@ static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape
if (test_bit(PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
dma_ok = !hwif->dma_setup(drive);
- if (IDE_CONTROL_REG)
- hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
- hwif->OUTB(dma_ok ? 1 : 0, IDE_FEATURE_REG); /* Use PIO/DMA */
- hwif->OUTB(bcount.b.high, IDE_BCOUNTH_REG);
- hwif->OUTB(bcount.b.low, IDE_BCOUNTL_REG);
- hwif->OUTB(drive->select.all, IDE_SELECT_REG);
+ ide_pktcmd_tf_load(drive, IDE_TFLAG_NO_SELECT_MASK |
+ IDE_TFLAG_OUT_DEVICE, bcount, dma_ok);
+
if (dma_ok) /* Will begin DMA later */
set_bit(PC_DMA_IN_PROGRESS, &pc->flags);
if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) {
@@ -2295,11 +2283,11 @@ static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive)
{
idetape_tape_t *tape = drive->driver_data;
idetape_pc_t *pc = tape->pc;
- atapi_status_t status;
+ u8 stat;
- status.all = HWIF(drive)->INB(IDE_STATUS_REG);
- if (status.b.dsc) {
- if (status.b.check) {
+ stat = drive->hwif->INB(IDE_STATUS_REG);
+ if (stat & SEEK_STAT) {
+ if (stat & ERR_STAT) {
/* Error detected */
if (pc->c[0] != IDETAPE_TEST_UNIT_READY_CMD)
printk(KERN_ERR "ide-tape: %s: I/O error, ",
@@ -2417,7 +2405,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
idetape_tape_t *tape = drive->driver_data;
idetape_pc_t *pc = NULL;
struct request *postponed_rq = tape->postponed_rq;
- atapi_status_t status;
+ u8 stat;
#if IDETAPE_DEBUG_LOG
#if 0
@@ -2465,7 +2453,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
* If the tape is still busy, postpone our request and service
* the other device meanwhile.
*/
- status.all = HWIF(drive)->INB(IDE_STATUS_REG);
+ stat = drive->hwif->INB(IDE_STATUS_REG);
if (!drive->dsc_overlap && !(rq->cmd[0] & REQ_IDETAPE_PC2))
set_bit(IDETAPE_IGNORE_DSC, &tape->flags);
@@ -2481,7 +2469,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
calculate_speeds(drive);
if (!test_and_clear_bit(IDETAPE_IGNORE_DSC, &tape->flags) &&
- !status.b.dsc) {
+ (stat & SEEK_STAT) == 0) {
if (postponed_rq == NULL) {
tape->dsc_polling_start = jiffies;
tape->dsc_polling_frequency = tape->best_dsc_rw_frequency;
@@ -2502,9 +2490,6 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
}
if (rq->cmd[0] & REQ_IDETAPE_READ) {
tape->buffer_head++;
-#if USE_IOTRACE
- IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
tape->postpone_cnt = 0;
pc = idetape_next_pc_storage(drive);
idetape_create_read_cmd(tape, pc, rq->current_nr_sectors, (struct idetape_bh *)rq->special);
@@ -2512,9 +2497,6 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
}
if (rq->cmd[0] & REQ_IDETAPE_WRITE) {
tape->buffer_head++;
-#if USE_IOTRACE
- IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
tape->postpone_cnt = 0;
pc = idetape_next_pc_storage(drive);
idetape_create_write_cmd(tape, pc, rq->current_nr_sectors, (struct idetape_bh *)rq->special);
@@ -3241,9 +3223,6 @@ static int idetape_add_chrdev_write_request (ide_drive_t *drive, int blocks)
idetape_switch_buffers(tape, new_stage);
idetape_add_stage_tail(drive, new_stage);
tape->pipeline_head++;
-#if USE_IOTRACE
- IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
calculate_speeds(drive);
/*
@@ -3493,9 +3472,6 @@ static int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks)
idetape_remove_stage_head(drive);
spin_unlock_irqrestore(&tape->spinlock, flags);
tape->pipeline_head++;
-#if USE_IOTRACE
- IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
calculate_speeds(drive);
}
#if IDETAPE_DEBUG_BUGS
diff --git a/trunk/drivers/ide/ide-taskfile.c b/trunk/drivers/ide/ide-taskfile.c
index 2b60f1b0437e..2d63ea9ee61b 100644
--- a/trunk/drivers/ide/ide-taskfile.c
+++ b/trunk/drivers/ide/ide-taskfile.c
@@ -63,65 +63,78 @@ static void taskfile_output_data(ide_drive_t *drive, void *buffer, u32 wcount)
}
}
+void ide_tf_load(ide_drive_t *drive, ide_task_t *task)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_taskfile *tf = &task->tf;
+ u8 HIHI = (task->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF;
+
+ if (task->tf_flags & IDE_TFLAG_FLAGGED)
+ HIHI = 0xFF;
+
+#ifdef DEBUG
+ printk("%s: tf: feat 0x%02x nsect 0x%02x lbal 0x%02x "
+ "lbam 0x%02x lbah 0x%02x dev 0x%02x cmd 0x%02x\n",
+ drive->name, tf->feature, tf->nsect, tf->lbal,
+ tf->lbam, tf->lbah, tf->device, tf->command);
+#endif
+
+ if (IDE_CONTROL_REG)
+ hwif->OUTB(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */
+
+ if ((task->tf_flags & IDE_TFLAG_NO_SELECT_MASK) == 0)
+ SELECT_MASK(drive, 0);
+
+ if (task->tf_flags & IDE_TFLAG_OUT_DATA)
+ hwif->OUTW((tf->hob_data << 8) | tf->data, IDE_DATA_REG);
+
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE)
+ hwif->OUTB(tf->hob_feature, IDE_FEATURE_REG);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_NSECT)
+ hwif->OUTB(tf->hob_nsect, IDE_NSECTOR_REG);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAL)
+ hwif->OUTB(tf->hob_lbal, IDE_SECTOR_REG);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAM)
+ hwif->OUTB(tf->hob_lbam, IDE_LCYL_REG);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAH)
+ hwif->OUTB(tf->hob_lbah, IDE_HCYL_REG);
+
+ if (task->tf_flags & IDE_TFLAG_OUT_FEATURE)
+ hwif->OUTB(tf->feature, IDE_FEATURE_REG);
+ if (task->tf_flags & IDE_TFLAG_OUT_NSECT)
+ hwif->OUTB(tf->nsect, IDE_NSECTOR_REG);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAL)
+ hwif->OUTB(tf->lbal, IDE_SECTOR_REG);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAM)
+ hwif->OUTB(tf->lbam, IDE_LCYL_REG);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAH)
+ hwif->OUTB(tf->lbah, IDE_HCYL_REG);
+
+ if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
+ hwif->OUTB((tf->device & HIHI) | drive->select.all, IDE_SELECT_REG);
+}
+
int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf)
{
ide_task_t args;
+
memset(&args, 0, sizeof(ide_task_t));
- args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01;
+ args.tf.nsect = 0x01;
if (drive->media == ide_disk)
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_IDENTIFY;
+ args.tf.command = WIN_IDENTIFY;
else
- args.tfRegister[IDE_COMMAND_OFFSET] = WIN_PIDENTIFY;
- args.command_type = IDE_DRIVE_TASK_IN;
- args.data_phase = TASKFILE_IN;
- args.handler = &task_in_intr;
- return ide_raw_taskfile(drive, &args, buf);
+ args.tf.command = WIN_PIDENTIFY;
+ args.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
+ args.data_phase = TASKFILE_IN;
+ return ide_raw_taskfile(drive, &args, buf, 1);
}
-ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
+static int inline task_dma_ok(ide_task_t *task)
{
- ide_hwif_t *hwif = HWIF(drive);
- task_struct_t *taskfile = (task_struct_t *) task->tfRegister;
- hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister;
- u8 HIHI = (drive->addressing == 1) ? 0xE0 : 0xEF;
-
- /* ALL Command Block Executions SHALL clear nIEN, unless otherwise */
- if (IDE_CONTROL_REG) {
- /* clear nIEN */
- hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
- }
- SELECT_MASK(drive, 0);
-
- if (drive->addressing == 1) {
- hwif->OUTB(hobfile->feature, IDE_FEATURE_REG);
- hwif->OUTB(hobfile->sector_count, IDE_NSECTOR_REG);
- hwif->OUTB(hobfile->sector_number, IDE_SECTOR_REG);
- hwif->OUTB(hobfile->low_cylinder, IDE_LCYL_REG);
- hwif->OUTB(hobfile->high_cylinder, IDE_HCYL_REG);
- }
-
- hwif->OUTB(taskfile->feature, IDE_FEATURE_REG);
- hwif->OUTB(taskfile->sector_count, IDE_NSECTOR_REG);
- hwif->OUTB(taskfile->sector_number, IDE_SECTOR_REG);
- hwif->OUTB(taskfile->low_cylinder, IDE_LCYL_REG);
- hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG);
-
- hwif->OUTB((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG);
-
- if (task->handler != NULL) {
- if (task->prehandler != NULL) {
- hwif->OUTBSYNC(drive, taskfile->command, IDE_COMMAND_REG);
- ndelay(400); /* FIXME */
- return task->prehandler(drive, task->rq);
- }
- ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL);
- return ide_started;
- }
-
- if (!drive->using_dma)
- return ide_stopped;
+ if (blk_fs_request(task->rq) || (task->tf_flags & IDE_TFLAG_FLAGGED))
+ return 1;
- switch (taskfile->command) {
+ switch (task->tf.command) {
case WIN_WRITEDMA_ONCE:
case WIN_WRITEDMA:
case WIN_WRITEDMA_EXT:
@@ -129,24 +142,79 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
case WIN_READDMA:
case WIN_READDMA_EXT:
case WIN_IDENTIFY_DMA:
- if (!hwif->dma_setup(drive)) {
- hwif->dma_exec_cmd(drive, taskfile->command);
- hwif->dma_start(drive);
- return ide_started;
- }
- break;
- default:
- if (task->handler == NULL)
- return ide_stopped;
+ return 1;
}
- return ide_stopped;
+ return 0;
+}
+
+static ide_startstop_t task_no_data_intr(ide_drive_t *);
+static ide_startstop_t set_geometry_intr(ide_drive_t *);
+static ide_startstop_t recal_intr(ide_drive_t *);
+static ide_startstop_t set_multmode_intr(ide_drive_t *);
+static ide_startstop_t pre_task_out_intr(ide_drive_t *, struct request *);
+static ide_startstop_t task_in_intr(ide_drive_t *);
+
+ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct ide_taskfile *tf = &task->tf;
+ ide_handler_t *handler = NULL;
+
+ if (task->data_phase == TASKFILE_MULTI_IN ||
+ task->data_phase == TASKFILE_MULTI_OUT) {
+ if (!drive->mult_count) {
+ printk(KERN_ERR "%s: multimode not set!\n",
+ drive->name);
+ return ide_stopped;
+ }
+ }
+
+ if (task->tf_flags & IDE_TFLAG_FLAGGED)
+ task->tf_flags |= IDE_TFLAG_FLAGGED_SET_IN_FLAGS;
+
+ if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0)
+ ide_tf_load(drive, task);
+
+ switch (task->data_phase) {
+ case TASKFILE_MULTI_OUT:
+ case TASKFILE_OUT:
+ hwif->OUTBSYNC(drive, tf->command, IDE_COMMAND_REG);
+ ndelay(400); /* FIXME */
+ return pre_task_out_intr(drive, task->rq);
+ case TASKFILE_MULTI_IN:
+ case TASKFILE_IN:
+ handler = task_in_intr;
+ /* fall-through */
+ case TASKFILE_NO_DATA:
+ if (handler == NULL)
+ handler = task_no_data_intr;
+ /* WIN_{SPECIFY,RESTORE,SETMULT} use custom handlers */
+ if (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) {
+ switch (tf->command) {
+ case WIN_SPECIFY: handler = set_geometry_intr; break;
+ case WIN_RESTORE: handler = recal_intr; break;
+ case WIN_SETMULT: handler = set_multmode_intr; break;
+ }
+ }
+ ide_execute_command(drive, tf->command, handler,
+ WAIT_WORSTCASE, NULL);
+ return ide_started;
+ default:
+ if (task_dma_ok(task) == 0 || drive->using_dma == 0 ||
+ hwif->dma_setup(drive))
+ return ide_stopped;
+ hwif->dma_exec_cmd(drive, tf->command);
+ hwif->dma_start(drive);
+ return ide_started;
+ }
}
+EXPORT_SYMBOL_GPL(do_rw_taskfile);
/*
* set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
*/
-ide_startstop_t set_multmode_intr (ide_drive_t *drive)
+static ide_startstop_t set_multmode_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
u8 stat;
@@ -164,7 +232,7 @@ ide_startstop_t set_multmode_intr (ide_drive_t *drive)
/*
* set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
*/
-ide_startstop_t set_geometry_intr (ide_drive_t *drive)
+static ide_startstop_t set_geometry_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
int retries = 5;
@@ -187,7 +255,7 @@ ide_startstop_t set_geometry_intr (ide_drive_t *drive)
/*
* recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
*/
-ide_startstop_t recal_intr (ide_drive_t *drive)
+static ide_startstop_t recal_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
u8 stat;
@@ -200,7 +268,7 @@ ide_startstop_t recal_intr (ide_drive_t *drive)
/*
* Handler for commands without a data phase
*/
-ide_startstop_t task_no_data_intr (ide_drive_t *drive)
+static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
{
ide_task_t *args = HWGROUP(drive)->rq->special;
ide_hwif_t *hwif = HWIF(drive);
@@ -217,8 +285,6 @@ ide_startstop_t task_no_data_intr (ide_drive_t *drive)
return ide_stopped;
}
-EXPORT_SYMBOL(task_no_data_intr);
-
static u8 wait_drive_not_busy(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
@@ -363,7 +429,7 @@ static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *task = rq->special;
- if (task->tf_out_flags.all) {
+ if (task->tf_flags & IDE_TFLAG_FLAGGED) {
u8 err = drive->hwif->INB(IDE_ERROR_REG);
ide_end_drive_cmd(drive, stat, err);
return;
@@ -382,7 +448,7 @@ static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
/*
* Handler for command with PIO data-in phase (Read/Read Multiple).
*/
-ide_startstop_t task_in_intr (ide_drive_t *drive)
+static ide_startstop_t task_in_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
struct request *rq = HWGROUP(drive)->rq;
@@ -413,7 +479,6 @@ ide_startstop_t task_in_intr (ide_drive_t *drive)
return ide_started;
}
-EXPORT_SYMBOL(task_in_intr);
/*
* Handler for command with PIO data-out phase (Write/Write Multiple).
@@ -443,7 +508,7 @@ static ide_startstop_t task_out_intr (ide_drive_t *drive)
return ide_started;
}
-ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq)
+static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
{
ide_startstop_t startstop;
@@ -464,9 +529,8 @@ ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq)
return ide_started;
}
-EXPORT_SYMBOL(pre_task_out_intr);
-static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long data_size, u8 *buf)
+int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *task, u8 *buf, u16 nsect)
{
struct request rq;
@@ -481,36 +545,27 @@ static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long
* if we would find a solution to transfer any size.
* To support special commands like READ LONG.
*/
- if (args->command_type != IDE_DRIVE_TASK_NO_DATA) {
- if (data_size == 0)
- rq.nr_sectors = (args->hobRegister[IDE_NSECTOR_OFFSET] << 8) | args->tfRegister[IDE_NSECTOR_OFFSET];
- else
- rq.nr_sectors = data_size / SECTOR_SIZE;
-
- if (!rq.nr_sectors) {
- printk(KERN_ERR "%s: in/out command without data\n",
- drive->name);
- return -EFAULT;
- }
+ rq.hard_nr_sectors = rq.nr_sectors = nsect;
+ rq.hard_cur_sectors = rq.current_nr_sectors = nsect;
- rq.hard_nr_sectors = rq.nr_sectors;
- rq.hard_cur_sectors = rq.current_nr_sectors = rq.nr_sectors;
+ if (task->tf_flags & IDE_TFLAG_WRITE)
+ rq.cmd_flags |= REQ_RW;
- if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
- rq.cmd_flags |= REQ_RW;
- }
+ rq.special = task;
+ task->rq = &rq;
- rq.special = args;
- args->rq = &rq;
return ide_do_drive_cmd(drive, &rq, ide_wait);
}
-int ide_raw_taskfile (ide_drive_t *drive, ide_task_t *args, u8 *buf)
+EXPORT_SYMBOL(ide_raw_taskfile);
+
+int ide_no_data_taskfile(ide_drive_t *drive, ide_task_t *task)
{
- return ide_diag_taskfile(drive, args, 0, buf);
-}
+ task->data_phase = TASKFILE_NO_DATA;
-EXPORT_SYMBOL(ide_raw_taskfile);
+ return ide_raw_taskfile(drive, task, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(ide_no_data_taskfile);
#ifdef CONFIG_IDE_TASK_IOCTL
int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
@@ -519,12 +574,12 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
ide_task_t args;
u8 *outbuf = NULL;
u8 *inbuf = NULL;
- task_ioreg_t *argsptr = args.tfRegister;
- task_ioreg_t *hobsptr = args.hobRegister;
+ u8 *data_buf = NULL;
int err = 0;
int tasksize = sizeof(struct ide_task_request_s);
unsigned int taskin = 0;
unsigned int taskout = 0;
+ u16 nsect = 0;
u8 io_32bit = drive->io_32bit;
char __user *buf = (char __user *)arg;
@@ -572,24 +627,52 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
}
memset(&args, 0, sizeof(ide_task_t));
- memcpy(argsptr, req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE);
- memcpy(hobsptr, req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE);
- args.tf_in_flags = req_task->in_flags;
- args.tf_out_flags = req_task->out_flags;
- args.data_phase = req_task->data_phase;
- args.command_type = req_task->req_cmd;
+ memcpy(&args.tf_array[0], req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE - 2);
+ memcpy(&args.tf_array[6], req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE);
+
+ args.data_phase = req_task->data_phase;
+
+ args.tf_flags = IDE_TFLAG_OUT_DEVICE;
+ if (drive->addressing == 1)
+ args.tf_flags |= IDE_TFLAG_LBA48;
+
+ if (req_task->out_flags.all) {
+ args.tf_flags |= IDE_TFLAG_FLAGGED;
+
+ if (req_task->out_flags.b.data)
+ args.tf_flags |= IDE_TFLAG_OUT_DATA;
+
+ if (req_task->out_flags.b.nsector_hob)
+ args.tf_flags |= IDE_TFLAG_OUT_HOB_NSECT;
+ if (req_task->out_flags.b.sector_hob)
+ args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAL;
+ if (req_task->out_flags.b.lcyl_hob)
+ args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAM;
+ if (req_task->out_flags.b.hcyl_hob)
+ args.tf_flags |= IDE_TFLAG_OUT_HOB_LBAH;
+
+ if (req_task->out_flags.b.error_feature)
+ args.tf_flags |= IDE_TFLAG_OUT_FEATURE;
+ if (req_task->out_flags.b.nsector)
+ args.tf_flags |= IDE_TFLAG_OUT_NSECT;
+ if (req_task->out_flags.b.sector)
+ args.tf_flags |= IDE_TFLAG_OUT_LBAL;
+ if (req_task->out_flags.b.lcyl)
+ args.tf_flags |= IDE_TFLAG_OUT_LBAM;
+ if (req_task->out_flags.b.hcyl)
+ args.tf_flags |= IDE_TFLAG_OUT_LBAH;
+ } else {
+ args.tf_flags |= IDE_TFLAG_OUT_TF;
+ if (args.tf_flags & IDE_TFLAG_LBA48)
+ args.tf_flags |= IDE_TFLAG_OUT_HOB;
+ }
+
+ if (req_task->in_flags.b.data)
+ args.tf_flags |= IDE_TFLAG_IN_DATA;
drive->io_32bit = 0;
switch(req_task->data_phase) {
- case TASKFILE_OUT_DMAQ:
- case TASKFILE_OUT_DMA:
- err = ide_diag_taskfile(drive, &args, taskout, outbuf);
- break;
- case TASKFILE_IN_DMAQ:
- case TASKFILE_IN_DMA:
- err = ide_diag_taskfile(drive, &args, taskin, inbuf);
- break;
case TASKFILE_MULTI_OUT:
if (!drive->mult_count) {
/* (hs): give up if multcount is not set */
@@ -601,9 +684,11 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
}
/* fall through */
case TASKFILE_OUT:
- args.prehandler = &pre_task_out_intr;
- args.handler = &task_out_intr;
- err = ide_diag_taskfile(drive, &args, taskout, outbuf);
+ /* fall through */
+ case TASKFILE_OUT_DMAQ:
+ case TASKFILE_OUT_DMA:
+ nsect = taskout / SECTOR_SIZE;
+ data_buf = outbuf;
break;
case TASKFILE_MULTI_IN:
if (!drive->mult_count) {
@@ -616,22 +701,46 @@ int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
}
/* fall through */
case TASKFILE_IN:
- args.handler = &task_in_intr;
- err = ide_diag_taskfile(drive, &args, taskin, inbuf);
+ /* fall through */
+ case TASKFILE_IN_DMAQ:
+ case TASKFILE_IN_DMA:
+ nsect = taskin / SECTOR_SIZE;
+ data_buf = inbuf;
break;
case TASKFILE_NO_DATA:
- args.handler = &task_no_data_intr;
- err = ide_diag_taskfile(drive, &args, 0, NULL);
break;
default:
err = -EFAULT;
goto abort;
}
- memcpy(req_task->io_ports, &(args.tfRegister), HDIO_DRIVE_TASK_HDR_SIZE);
- memcpy(req_task->hob_ports, &(args.hobRegister), HDIO_DRIVE_HOB_HDR_SIZE);
- req_task->in_flags = args.tf_in_flags;
- req_task->out_flags = args.tf_out_flags;
+ if (req_task->req_cmd == IDE_DRIVE_TASK_NO_DATA)
+ nsect = 0;
+ else if (!nsect) {
+ nsect = (args.tf.hob_nsect << 8) | args.tf.nsect;
+
+ if (!nsect) {
+ printk(KERN_ERR "%s: in/out command without data\n",
+ drive->name);
+ err = -EFAULT;
+ goto abort;
+ }
+ }
+
+ if (req_task->req_cmd == IDE_DRIVE_TASK_RAW_WRITE)
+ args.tf_flags |= IDE_TFLAG_WRITE;
+
+ err = ide_raw_taskfile(drive, &args, data_buf, nsect);
+
+ memcpy(req_task->hob_ports, &args.tf_array[0], HDIO_DRIVE_HOB_HDR_SIZE - 2);
+ memcpy(req_task->io_ports, &args.tf_array[6], HDIO_DRIVE_TASK_HDR_SIZE);
+
+ if ((args.tf_flags & IDE_TFLAG_FLAGGED_SET_IN_FLAGS) &&
+ req_task->in_flags.all == 0) {
+ req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS;
+ if (drive->addressing == 1)
+ req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
+ }
if (copy_to_user(buf, req_task, tasksize)) {
err = -EFAULT;
@@ -688,6 +797,7 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
u8 xfer_rate = 0;
int argsize = 4;
ide_task_t tfargs;
+ struct ide_taskfile *tf = &tfargs.tf;
if (NULL == (void *) arg) {
struct request rq;
@@ -699,13 +809,10 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
return -EFAULT;
memset(&tfargs, 0, sizeof(ide_task_t));
- tfargs.tfRegister[IDE_FEATURE_OFFSET] = args[2];
- tfargs.tfRegister[IDE_NSECTOR_OFFSET] = args[3];
- tfargs.tfRegister[IDE_SECTOR_OFFSET] = args[1];
- tfargs.tfRegister[IDE_LCYL_OFFSET] = 0x00;
- tfargs.tfRegister[IDE_HCYL_OFFSET] = 0x00;
- tfargs.tfRegister[IDE_SELECT_OFFSET] = 0x00;
- tfargs.tfRegister[IDE_COMMAND_OFFSET] = args[0];
+ tf->feature = args[2];
+ tf->nsect = args[3];
+ tf->lbal = args[1];
+ tf->command = args[0];
if (args[3]) {
argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
@@ -734,135 +841,28 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
return err;
}
-static int ide_wait_cmd_task(ide_drive_t *drive, u8 *buf)
-{
- struct request rq;
-
- ide_init_drive_cmd(&rq);
- rq.cmd_type = REQ_TYPE_ATA_TASK;
- rq.buffer = buf;
- return ide_do_drive_cmd(drive, &rq, ide_wait);
-}
-
int ide_task_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
{
void __user *p = (void __user *)arg;
int err = 0;
- u8 args[7], *argbuf = args;
- int argsize = 7;
+ u8 args[7];
+ ide_task_t task;
if (copy_from_user(args, p, 7))
return -EFAULT;
- err = ide_wait_cmd_task(drive, argbuf);
- if (copy_to_user(p, argbuf, argsize))
- err = -EFAULT;
- return err;
-}
-
-/*
- * NOTICE: This is additions from IBM to provide a discrete interface,
- * for selective taskregister access operations. Nice JOB Klaus!!!
- * Glad to be able to work and co-develop this with you and IBM.
- */
-ide_startstop_t flagged_taskfile (ide_drive_t *drive, ide_task_t *task)
-{
- ide_hwif_t *hwif = HWIF(drive);
- task_struct_t *taskfile = (task_struct_t *) task->tfRegister;
- hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister;
-
- if (task->data_phase == TASKFILE_MULTI_IN ||
- task->data_phase == TASKFILE_MULTI_OUT) {
- if (!drive->mult_count) {
- printk(KERN_ERR "%s: multimode not set!\n", drive->name);
- return ide_stopped;
- }
- }
-
- /*
- * (ks) Check taskfile in flags.
- * If set, then execute as it is defined.
- * If not set, then define default settings.
- * The default values are:
- * read all taskfile registers (except data)
- * read the hob registers (sector, nsector, lcyl, hcyl)
- */
- if (task->tf_in_flags.all == 0) {
- task->tf_in_flags.all = IDE_TASKFILE_STD_IN_FLAGS;
- if (drive->addressing == 1)
- task->tf_in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
- }
-
- /* ALL Command Block Executions SHALL clear nIEN, unless otherwise */
- if (IDE_CONTROL_REG)
- /* clear nIEN */
- hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
- SELECT_MASK(drive, 0);
-
- if (task->tf_out_flags.b.data) {
- u16 data = taskfile->data + (hobfile->data << 8);
- hwif->OUTW(data, IDE_DATA_REG);
- }
-
- /* (ks) send hob registers first */
- if (task->tf_out_flags.b.nsector_hob)
- hwif->OUTB(hobfile->sector_count, IDE_NSECTOR_REG);
- if (task->tf_out_flags.b.sector_hob)
- hwif->OUTB(hobfile->sector_number, IDE_SECTOR_REG);
- if (task->tf_out_flags.b.lcyl_hob)
- hwif->OUTB(hobfile->low_cylinder, IDE_LCYL_REG);
- if (task->tf_out_flags.b.hcyl_hob)
- hwif->OUTB(hobfile->high_cylinder, IDE_HCYL_REG);
-
- /* (ks) Send now the standard registers */
- if (task->tf_out_flags.b.error_feature)
- hwif->OUTB(taskfile->feature, IDE_FEATURE_REG);
- /* refers to number of sectors to transfer */
- if (task->tf_out_flags.b.nsector)
- hwif->OUTB(taskfile->sector_count, IDE_NSECTOR_REG);
- /* refers to sector offset or start sector */
- if (task->tf_out_flags.b.sector)
- hwif->OUTB(taskfile->sector_number, IDE_SECTOR_REG);
- if (task->tf_out_flags.b.lcyl)
- hwif->OUTB(taskfile->low_cylinder, IDE_LCYL_REG);
- if (task->tf_out_flags.b.hcyl)
- hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG);
-
- /*
- * (ks) In the flagged taskfile approch, we will use all specified
- * registers and the register value will not be changed, except the
- * select bit (master/slave) in the drive_head register. We must make
- * sure that the desired drive is selected.
- */
- hwif->OUTB(taskfile->device_head | drive->select.all, IDE_SELECT_REG);
- switch(task->data_phase) {
- case TASKFILE_OUT_DMAQ:
- case TASKFILE_OUT_DMA:
- case TASKFILE_IN_DMAQ:
- case TASKFILE_IN_DMA:
- if (!drive->using_dma)
- break;
+ memset(&task, 0, sizeof(task));
+ memcpy(&task.tf_array[7], &args[1], 6);
+ task.tf.command = args[0];
+ task.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE;
- if (!hwif->dma_setup(drive)) {
- hwif->dma_exec_cmd(drive, taskfile->command);
- hwif->dma_start(drive);
- return ide_started;
- }
- break;
+ err = ide_no_data_taskfile(drive, &task);
- default:
- if (task->handler == NULL)
- return ide_stopped;
+ args[0] = task.tf.command;
+ memcpy(&args[1], &task.tf_array[7], 6);
- /* Issue the command */
- if (task->prehandler) {
- hwif->OUTBSYNC(drive, taskfile->command, IDE_COMMAND_REG);
- ndelay(400); /* FIXME */
- return task->prehandler(drive, task->rq);
- }
- ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL);
- return ide_started;
- }
+ if (copy_to_user(p, args, 7))
+ err = -EFAULT;
- return ide_stopped;
+ return err;
}
diff --git a/trunk/drivers/ide/ide.c b/trunk/drivers/ide/ide.c
index 54943da6e4e5..c6d4f630e18a 100644
--- a/trunk/drivers/ide/ide.c
+++ b/trunk/drivers/ide/ide.c
@@ -424,7 +424,6 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
hwif->reset_poll = tmp_hwif->reset_poll;
hwif->pre_reset = tmp_hwif->pre_reset;
hwif->resetproc = tmp_hwif->resetproc;
- hwif->intrproc = tmp_hwif->intrproc;
hwif->maskproc = tmp_hwif->maskproc;
hwif->quirkproc = tmp_hwif->quirkproc;
hwif->busproc = tmp_hwif->busproc;
@@ -468,7 +467,6 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
#endif
hwif->dma_base = tmp_hwif->dma_base;
- hwif->dma_master = tmp_hwif->dma_master;
hwif->dma_command = tmp_hwif->dma_command;
hwif->dma_vendor1 = tmp_hwif->dma_vendor1;
hwif->dma_status = tmp_hwif->dma_status;
@@ -602,7 +600,6 @@ void ide_unregister(unsigned int index)
(void) ide_release_dma(hwif);
hwif->dma_base = 0;
- hwif->dma_master = 0;
hwif->dma_command = 0;
hwif->dma_vendor1 = 0;
hwif->dma_status = 0;
@@ -854,8 +851,7 @@ int set_using_dma(ide_drive_t *drive, int arg)
err = 0;
if (arg) {
- hwif->dma_off_quietly(drive);
- if (ide_set_dma(drive) || hwif->ide_dma_on(drive))
+ if (ide_set_dma(drive))
err = -EIO;
} else
ide_dma_off(drive);
diff --git a/trunk/drivers/ide/mips/au1xxx-ide.c b/trunk/drivers/ide/mips/au1xxx-ide.c
index a4ce3ba15d61..a4d0d4ca73d0 100644
--- a/trunk/drivers/ide/mips/au1xxx-ide.c
+++ b/trunk/drivers/ide/mips/au1xxx-ide.c
@@ -198,8 +198,6 @@ static void auide_set_dma_mode(ide_drive_t *drive, const u8 speed)
break;
#endif
- default:
- return;
}
au_writel(mem_sttime,MEM_STTIME2);
diff --git a/trunk/drivers/ide/pci/aec62xx.c b/trunk/drivers/ide/pci/aec62xx.c
index 44268504ae43..7f4d1857d555 100644
--- a/trunk/drivers/ide/pci/aec62xx.c
+++ b/trunk/drivers/ide/pci/aec62xx.c
@@ -202,6 +202,7 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
.host_flags = IDE_HFLAG_SERIALIZE |
IDE_HFLAG_NO_ATAPI_DMA |
+ IDE_HFLAG_ABUSE_SET_DMA_MODE |
IDE_HFLAG_OFF_BOARD,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
@@ -211,6 +212,7 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.init_chipset = init_chipset_aec62xx,
.init_hwif = init_hwif_aec62xx,
.host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_AUTODMA |
+ IDE_HFLAG_ABUSE_SET_DMA_MODE |
IDE_HFLAG_OFF_BOARD,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
@@ -220,7 +222,8 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.init_chipset = init_chipset_aec62xx,
.init_hwif = init_hwif_aec62xx,
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA,
+ .host_flags = IDE_HFLAG_NO_ATAPI_DMA |
+ IDE_HFLAG_ABUSE_SET_DMA_MODE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA4,
@@ -228,7 +231,9 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.name = "AEC6280",
.init_chipset = init_chipset_aec62xx,
.init_hwif = init_hwif_aec62xx,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAG_NO_ATAPI_DMA |
+ IDE_HFLAG_ABUSE_SET_DMA_MODE |
+ IDE_HFLAG_OFF_BOARD,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -237,7 +242,9 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.init_chipset = init_chipset_aec62xx,
.init_hwif = init_hwif_aec62xx,
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAG_NO_ATAPI_DMA |
+ IDE_HFLAG_ABUSE_SET_DMA_MODE |
+ IDE_HFLAG_OFF_BOARD,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
diff --git a/trunk/drivers/ide/pci/alim15x3.c b/trunk/drivers/ide/pci/alim15x3.c
index ce293936af4b..49aa82e412b6 100644
--- a/trunk/drivers/ide/pci/alim15x3.c
+++ b/trunk/drivers/ide/pci/alim15x3.c
@@ -402,9 +402,6 @@ static void ali_set_dma_mode(ide_drive_t *drive, const u8 speed)
u8 tmpbyte = 0x00;
int m5229_udma = (hwif->channel) ? 0x57 : 0x56;
- if (speed < XFER_PIO_0)
- return;
-
if (speed == XFER_UDMA_6)
speed1 = 0x47;
diff --git a/trunk/drivers/ide/pci/amd74xx.c b/trunk/drivers/ide/pci/amd74xx.c
index 8d4125ec252c..cee51fdafcf6 100644
--- a/trunk/drivers/ide/pci/amd74xx.c
+++ b/trunk/drivers/ide/pci/amd74xx.c
@@ -266,6 +266,7 @@ static void __devinit init_hwif_amd74xx(ide_hwif_t *hwif)
#define IDE_HFLAGS_AMD \
(IDE_HFLAG_PIO_NO_BLACKLIST | \
IDE_HFLAG_PIO_NO_DOWNGRADE | \
+ IDE_HFLAG_ABUSE_SET_DMA_MODE | \
IDE_HFLAG_POST_SET_MODE | \
IDE_HFLAG_IO_32BIT | \
IDE_HFLAG_UNMASK_IRQS | \
diff --git a/trunk/drivers/ide/pci/atiixp.c b/trunk/drivers/ide/pci/atiixp.c
index ef8e0164ef7a..5ae26564fb72 100644
--- a/trunk/drivers/ide/pci/atiixp.c
+++ b/trunk/drivers/ide/pci/atiixp.c
@@ -133,9 +133,6 @@ static void atiixp_set_dma_mode(ide_drive_t *drive, const u8 speed)
u32 tmp32;
u16 tmp16;
- if (speed < XFER_MW_DMA_0)
- return;
-
spin_lock_irqsave(&atiixp_lock, flags);
save_mdma_mode[drive->dn] = 0;
diff --git a/trunk/drivers/ide/pci/cmd64x.c b/trunk/drivers/ide/pci/cmd64x.c
index bc553337b1be..0b1e9479f019 100644
--- a/trunk/drivers/ide/pci/cmd64x.c
+++ b/trunk/drivers/ide/pci/cmd64x.c
@@ -322,8 +322,6 @@ static void cmd64x_set_dma_mode(ide_drive_t *drive, const u8 speed)
case XFER_MW_DMA_0:
program_cycle_times(drive, 480, 215);
break;
- default:
- return;
}
if (speed >= XFER_SW_DMA_0)
@@ -333,14 +331,15 @@ static void cmd64x_set_dma_mode(ide_drive_t *drive, const u8 speed)
static int cmd648_ide_dma_end (ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
+ unsigned long base = hwif->dma_base - (hwif->channel * 8);
int err = __ide_dma_end(drive);
u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 :
MRDMODE_INTR_CH0;
- u8 mrdmode = inb(hwif->dma_master + 0x01);
+ u8 mrdmode = inb(base + 1);
/* clear the interrupt bit */
outb((mrdmode & ~(MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1)) | irq_mask,
- hwif->dma_master + 0x01);
+ base + 1);
return err;
}
@@ -365,10 +364,11 @@ static int cmd64x_ide_dma_end (ide_drive_t *drive)
static int cmd648_ide_dma_test_irq (ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
+ unsigned long base = hwif->dma_base - (hwif->channel * 8);
u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 :
MRDMODE_INTR_CH0;
u8 dma_stat = inb(hwif->dma_status);
- u8 mrdmode = inb(hwif->dma_master + 0x01);
+ u8 mrdmode = inb(base + 1);
#ifdef DEBUG
printk("%s: dma_stat: 0x%02x mrdmode: 0x%02x irq_mask: 0x%02x\n",
diff --git a/trunk/drivers/ide/pci/cs5520.c b/trunk/drivers/ide/pci/cs5520.c
index 0466462fd21b..d1a91bcb5b29 100644
--- a/trunk/drivers/ide/pci/cs5520.c
+++ b/trunk/drivers/ide/pci/cs5520.c
@@ -137,6 +137,7 @@ static void __devinit init_hwif_cs5520(ide_hwif_t *hwif)
IDE_HFLAG_CS5520 | \
IDE_HFLAG_VDMA | \
IDE_HFLAG_NO_ATAPI_DMA | \
+ IDE_HFLAG_ABUSE_SET_DMA_MODE |\
IDE_HFLAG_BOOTABLE, \
.pio_mask = ATA_PIO4, \
}
diff --git a/trunk/drivers/ide/pci/cs5530.c b/trunk/drivers/ide/pci/cs5530.c
index 547690395eee..df5966b33460 100644
--- a/trunk/drivers/ide/pci/cs5530.c
+++ b/trunk/drivers/ide/pci/cs5530.c
@@ -116,8 +116,6 @@ static void cs5530_set_dma_mode(ide_drive_t *drive, const u8 mode)
case XFER_MW_DMA_0: timings = 0x00077771; break;
case XFER_MW_DMA_1: timings = 0x00012121; break;
case XFER_MW_DMA_2: timings = 0x00002020; break;
- default:
- return;
}
basereg = CS5530_BASEREG(drive->hwif);
reg = inl(basereg + 4); /* get drive0 config register */
diff --git a/trunk/drivers/ide/pci/cs5535.c b/trunk/drivers/ide/pci/cs5535.c
index ddcbeba671e1..50b3d7791f55 100644
--- a/trunk/drivers/ide/pci/cs5535.c
+++ b/trunk/drivers/ide/pci/cs5535.c
@@ -190,7 +190,7 @@ static const struct ide_port_info cs5535_chipset __devinitdata = {
.name = "CS5535",
.init_hwif = init_hwif_cs5535,
.host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE |
- IDE_HFLAG_BOOTABLE,
+ IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_BOOTABLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA4,
diff --git a/trunk/drivers/ide/pci/hpt34x.c b/trunk/drivers/ide/pci/hpt34x.c
index ae6307fae4f9..dfba0d13fcd3 100644
--- a/trunk/drivers/ide/pci/hpt34x.c
+++ b/trunk/drivers/ide/pci/hpt34x.c
@@ -129,14 +129,18 @@ static void __devinit init_hwif_hpt34x(ide_hwif_t *hwif)
hwif->set_dma_mode = &hpt34x_set_mode;
}
+#define IDE_HFLAGS_HPT34X \
+ (IDE_HFLAG_NO_ATAPI_DMA | \
+ IDE_HFLAG_ABUSE_SET_DMA_MODE | \
+ IDE_HFLAG_NO_AUTODMA)
+
static const struct ide_port_info hpt34x_chipsets[] __devinitdata = {
{ /* 0 */
.name = "HPT343",
.init_chipset = init_chipset_hpt34x,
.init_hwif = init_hwif_hpt34x,
.extra = 16,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA |
- IDE_HFLAG_NO_AUTODMA,
+ .host_flags = IDE_HFLAGS_HPT34X,
.pio_mask = ATA_PIO5,
},
{ /* 1 */
@@ -144,9 +148,7 @@ static const struct ide_port_info hpt34x_chipsets[] __devinitdata = {
.init_chipset = init_chipset_hpt34x,
.init_hwif = init_hwif_hpt34x,
.extra = 16,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA |
- IDE_HFLAG_NO_AUTODMA |
- IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAGS_HPT34X | IDE_HFLAG_OFF_BOARD,
.pio_mask = ATA_PIO5,
#ifdef CONFIG_HPT34X_AUTODMA
.swdma_mask = ATA_SWDMA2,
diff --git a/trunk/drivers/ide/pci/hpt366.c b/trunk/drivers/ide/pci/hpt366.c
index 9fce25bdec8a..3777fb8c8043 100644
--- a/trunk/drivers/ide/pci/hpt366.c
+++ b/trunk/drivers/ide/pci/hpt366.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/ide/pci/hpt366.c Version 1.22 Dec 4, 2007
+ * linux/drivers/ide/pci/hpt366.c Version 1.30 Dec 12, 2007
*
* Copyright (C) 1999-2003 Andre Hedrick
* Portions Copyright (C) 2001 Sun Microsystems, Inc.
@@ -88,7 +88,7 @@
* - rename all the register related variables consistently
* - move all the interrupt twiddling code from the speedproc handlers into
* init_hwif_hpt366(), also grouping all the DMA related code together there
- * - merge two HPT37x speedproc handlers, fix the PIO timing register mask and
+ * - merge HPT36x/HPT37x speedproc handlers, fix PIO timing register mask and
* separate the UltraDMA and MWDMA masks there to avoid changing PIO timings
* when setting an UltraDMA mode
* - fix hpt3xx_tune_drive() to set the PIO mode requested, not always select
@@ -458,6 +458,13 @@ enum ata_clock {
NUM_ATA_CLOCKS
};
+struct hpt_timings {
+ u32 pio_mask;
+ u32 dma_mask;
+ u32 ultra_mask;
+ u32 *clock_table[NUM_ATA_CLOCKS];
+};
+
/*
* Hold all the HighPoint chip information in one place.
*/
@@ -468,7 +475,8 @@ struct hpt_info {
u8 udma_mask; /* Allowed UltraDMA modes mask. */
u8 dpll_clk; /* DPLL clock in MHz */
u8 pci_clk; /* PCI clock in MHz */
- u32 **settings; /* Chipset settings table */
+ struct hpt_timings *timings; /* Chipset timing data */
+ u8 clock; /* ATA clock selected */
};
/* Supported HighPoint chips */
@@ -486,20 +494,30 @@ enum {
HPT371N
};
-static u32 *hpt36x_settings[NUM_ATA_CLOCKS] = {
- twenty_five_base_hpt36x,
- thirty_three_base_hpt36x,
- forty_base_hpt36x,
- NULL,
- NULL
+static struct hpt_timings hpt36x_timings = {
+ .pio_mask = 0xc1f8ffff,
+ .dma_mask = 0x303800ff,
+ .ultra_mask = 0x30070000,
+ .clock_table = {
+ [ATA_CLOCK_25MHZ] = twenty_five_base_hpt36x,
+ [ATA_CLOCK_33MHZ] = thirty_three_base_hpt36x,
+ [ATA_CLOCK_40MHZ] = forty_base_hpt36x,
+ [ATA_CLOCK_50MHZ] = NULL,
+ [ATA_CLOCK_66MHZ] = NULL
+ }
};
-static u32 *hpt37x_settings[NUM_ATA_CLOCKS] = {
- NULL,
- thirty_three_base_hpt37x,
- NULL,
- fifty_base_hpt37x,
- sixty_six_base_hpt37x
+static struct hpt_timings hpt37x_timings = {
+ .pio_mask = 0xcfc3ffff,
+ .dma_mask = 0x31c001ff,
+ .ultra_mask = 0x303c0000,
+ .clock_table = {
+ [ATA_CLOCK_25MHZ] = NULL,
+ [ATA_CLOCK_33MHZ] = thirty_three_base_hpt37x,
+ [ATA_CLOCK_40MHZ] = NULL,
+ [ATA_CLOCK_50MHZ] = fifty_base_hpt37x,
+ [ATA_CLOCK_66MHZ] = sixty_six_base_hpt37x
+ }
};
static const struct hpt_info hpt36x __devinitdata = {
@@ -507,7 +525,7 @@ static const struct hpt_info hpt36x __devinitdata = {
.chip_type = HPT36x,
.udma_mask = HPT366_ALLOW_ATA66_3 ? (HPT366_ALLOW_ATA66_4 ? ATA_UDMA4 : ATA_UDMA3) : ATA_UDMA2,
.dpll_clk = 0, /* no DPLL */
- .settings = hpt36x_settings
+ .timings = &hpt36x_timings
};
static const struct hpt_info hpt370 __devinitdata = {
@@ -515,7 +533,7 @@ static const struct hpt_info hpt370 __devinitdata = {
.chip_type = HPT370,
.udma_mask = HPT370_ALLOW_ATA100_5 ? ATA_UDMA5 : ATA_UDMA4,
.dpll_clk = 48,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt370a __devinitdata = {
@@ -523,7 +541,7 @@ static const struct hpt_info hpt370a __devinitdata = {
.chip_type = HPT370A,
.udma_mask = HPT370_ALLOW_ATA100_5 ? ATA_UDMA5 : ATA_UDMA4,
.dpll_clk = 48,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt374 __devinitdata = {
@@ -531,7 +549,7 @@ static const struct hpt_info hpt374 __devinitdata = {
.chip_type = HPT374,
.udma_mask = ATA_UDMA5,
.dpll_clk = 48,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt372 __devinitdata = {
@@ -539,7 +557,7 @@ static const struct hpt_info hpt372 __devinitdata = {
.chip_type = HPT372,
.udma_mask = HPT372_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5,
.dpll_clk = 55,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt372a __devinitdata = {
@@ -547,7 +565,7 @@ static const struct hpt_info hpt372a __devinitdata = {
.chip_type = HPT372A,
.udma_mask = HPT372_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5,
.dpll_clk = 66,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt302 __devinitdata = {
@@ -555,7 +573,7 @@ static const struct hpt_info hpt302 __devinitdata = {
.chip_type = HPT302,
.udma_mask = HPT302_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5,
.dpll_clk = 66,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt371 __devinitdata = {
@@ -563,7 +581,7 @@ static const struct hpt_info hpt371 __devinitdata = {
.chip_type = HPT371,
.udma_mask = HPT371_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5,
.dpll_clk = 66,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt372n __devinitdata = {
@@ -571,7 +589,7 @@ static const struct hpt_info hpt372n __devinitdata = {
.chip_type = HPT372N,
.udma_mask = HPT372_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5,
.dpll_clk = 77,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt302n __devinitdata = {
@@ -579,7 +597,7 @@ static const struct hpt_info hpt302n __devinitdata = {
.chip_type = HPT302N,
.udma_mask = HPT302_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5,
.dpll_clk = 77,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static const struct hpt_info hpt371n __devinitdata = {
@@ -587,7 +605,7 @@ static const struct hpt_info hpt371n __devinitdata = {
.chip_type = HPT371N,
.udma_mask = HPT371_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5,
.dpll_clk = 77,
- .settings = hpt37x_settings
+ .timings = &hpt37x_timings
};
static int check_in_drive_list(ide_drive_t *drive, const char **list)
@@ -675,71 +693,33 @@ static u32 get_speed_setting(u8 speed, struct hpt_info *info)
for (i = 0; i < ARRAY_SIZE(xfer_speeds) - 1; i++)
if (xfer_speeds[i] == speed)
break;
- /*
- * NOTE: info->settings only points to the pointer
- * to the list of the actual register values
- */
- return (*info->settings)[i];
+
+ return info->timings->clock_table[info->clock][i];
}
-static void hpt36x_set_mode(ide_drive_t *drive, const u8 speed)
+static void hpt3xx_set_mode(ide_drive_t *drive, const u8 speed)
{
- ide_hwif_t *hwif = HWIF(drive);
- struct pci_dev *dev = hwif->pci_dev;
+ struct pci_dev *dev = HWIF(drive)->pci_dev;
struct hpt_info *info = pci_get_drvdata(dev);
- u8 itr_addr = drive->dn ? 0x44 : 0x40;
+ struct hpt_timings *t = info->timings;
+ u8 itr_addr = 0x40 + (drive->dn * 4);
u32 old_itr = 0;
- u32 itr_mask, new_itr;
-
- itr_mask = speed < XFER_MW_DMA_0 ? 0x30070000 :
- (speed < XFER_UDMA_0 ? 0xc0070000 : 0xc03800ff);
-
- new_itr = get_speed_setting(speed, info);
+ u32 new_itr = get_speed_setting(speed, info);
+ u32 itr_mask = speed < XFER_MW_DMA_0 ? t->pio_mask :
+ (speed < XFER_UDMA_0 ? t->dma_mask :
+ t->ultra_mask);
+ pci_read_config_dword(dev, itr_addr, &old_itr);
+ new_itr = (old_itr & ~itr_mask) | (new_itr & itr_mask);
/*
* Disable on-chip PIO FIFO/buffer (and PIO MST mode as well)
* to avoid problems handling I/O errors later
*/
- pci_read_config_dword(dev, itr_addr, &old_itr);
- new_itr = (new_itr & ~itr_mask) | (old_itr & itr_mask);
new_itr &= ~0xc0000000;
pci_write_config_dword(dev, itr_addr, new_itr);
}
-static void hpt37x_set_mode(ide_drive_t *drive, const u8 speed)
-{
- ide_hwif_t *hwif = HWIF(drive);
- struct pci_dev *dev = hwif->pci_dev;
- struct hpt_info *info = pci_get_drvdata(dev);
- u8 itr_addr = 0x40 + (drive->dn * 4);
- u32 old_itr = 0;
- u32 itr_mask, new_itr;
-
- itr_mask = speed < XFER_MW_DMA_0 ? 0x303c0000 :
- (speed < XFER_UDMA_0 ? 0xc03c0000 : 0xc1c001ff);
-
- new_itr = get_speed_setting(speed, info);
-
- pci_read_config_dword(dev, itr_addr, &old_itr);
- new_itr = (new_itr & ~itr_mask) | (old_itr & itr_mask);
-
- if (speed < XFER_MW_DMA_0)
- new_itr &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */
- pci_write_config_dword(dev, itr_addr, new_itr);
-}
-
-static void hpt3xx_set_mode(ide_drive_t *drive, const u8 speed)
-{
- ide_hwif_t *hwif = HWIF(drive);
- struct hpt_info *info = pci_get_drvdata(hwif->pci_dev);
-
- if (info->chip_type >= HPT370)
- hpt37x_set_mode(drive, speed);
- else /* hpt368: hpt_minimum_revision(dev, 2) */
- hpt36x_set_mode(drive, speed);
-}
-
static void hpt3xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
hpt3xx_set_mode(drive, XFER_PIO_0 + pio);
@@ -756,15 +736,6 @@ static int hpt3xx_quirkproc(ide_drive_t *drive)
return 0;
}
-static void hpt3xx_intrproc(ide_drive_t *drive)
-{
- if (drive->quirk_list)
- return;
-
- /* drives in the quirk_list may not like intr setups/cleanups */
- outb(drive->ctl | 2, IDE_CONTROL_REG);
-}
-
static void hpt3xx_maskproc(ide_drive_t *drive, int mask)
{
ide_hwif_t *hwif = HWIF(drive);
@@ -914,32 +885,33 @@ static int hpt374_ide_dma_end(ide_drive_t *drive)
static void hpt3xxn_set_clock(ide_hwif_t *hwif, u8 mode)
{
- u8 scr2 = inb(hwif->dma_master + 0x7b);
+ unsigned long base = hwif->extra_base;
+ u8 scr2 = inb(base + 0x6b);
if ((scr2 & 0x7f) == mode)
return;
/* Tristate the bus */
- outb(0x80, hwif->dma_master + 0x73);
- outb(0x80, hwif->dma_master + 0x77);
+ outb(0x80, base + 0x63);
+ outb(0x80, base + 0x67);
/* Switch clock and reset channels */
- outb(mode, hwif->dma_master + 0x7b);
- outb(0xc0, hwif->dma_master + 0x79);
+ outb(mode, base + 0x6b);
+ outb(0xc0, base + 0x69);
/*
* Reset the state machines.
* NOTE: avoid accidentally enabling the disabled channels.
*/
- outb(inb(hwif->dma_master + 0x70) | 0x32, hwif->dma_master + 0x70);
- outb(inb(hwif->dma_master + 0x74) | 0x32, hwif->dma_master + 0x74);
+ outb(inb(base + 0x60) | 0x32, base + 0x60);
+ outb(inb(base + 0x64) | 0x32, base + 0x64);
/* Complete reset */
- outb(0x00, hwif->dma_master + 0x79);
+ outb(0x00, base + 0x69);
/* Reconnect channels to bus */
- outb(0x00, hwif->dma_master + 0x73);
- outb(0x00, hwif->dma_master + 0x77);
+ outb(0x00, base + 0x63);
+ outb(0x00, base + 0x67);
}
/**
@@ -1210,7 +1182,7 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
* We also don't like using the DPLL because this causes glitches
* on PRST-/SRST- when the state engine gets reset...
*/
- if (chip_type >= HPT374 || info->settings[clock] == NULL) {
+ if (chip_type >= HPT374 || info->timings->clock_table[clock] == NULL) {
u16 f_low, delta = pci_clk < 50 ? 2 : 4;
int adjust;
@@ -1226,7 +1198,7 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
clock = ATA_CLOCK_50MHZ;
}
- if (info->settings[clock] == NULL) {
+ if (info->timings->clock_table[clock] == NULL) {
printk(KERN_ERR "%s: unknown bus timing!\n", name);
kfree(info);
return -EIO;
@@ -1267,15 +1239,10 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
printk("%s: using %d MHz PCI clock\n", name, pci_clk);
}
- /*
- * Advance the table pointer to a slot which points to the list
- * of the register values settings matching the clock being used.
- */
- info->settings += clock;
-
/* Store the clock frequencies. */
info->dpll_clk = dpll_clk;
info->pci_clk = pci_clk;
+ info->clock = clock;
/* Point to this chip's own instance of the hpt_info structure. */
pci_set_drvdata(dev, info);
@@ -1320,8 +1287,8 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
hwif->set_pio_mode = &hpt3xx_set_pio_mode;
hwif->set_dma_mode = &hpt3xx_set_mode;
+
hwif->quirkproc = &hpt3xx_quirkproc;
- hwif->intrproc = &hpt3xx_intrproc;
hwif->maskproc = &hpt3xx_maskproc;
hwif->busproc = &hpt3xx_busproc;
@@ -1494,6 +1461,11 @@ static int __devinit hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2)
return 0;
}
+#define IDE_HFLAGS_HPT3XX \
+ (IDE_HFLAG_NO_ATAPI_DMA | \
+ IDE_HFLAG_ABUSE_SET_DMA_MODE | \
+ IDE_HFLAG_OFF_BOARD)
+
static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
{ /* 0 */
.name = "HPT36x",
@@ -1508,9 +1480,7 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
*/
.enablebits = {{0x50,0x10,0x10}, {0x54,0x04,0x04}},
.extra = 240,
- .host_flags = IDE_HFLAG_SINGLE |
- IDE_HFLAG_NO_ATAPI_DMA |
- IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAGS_HPT3XX | IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
},{ /* 1 */
@@ -1520,7 +1490,7 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
.init_dma = init_dma_hpt366,
.enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
.extra = 240,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAGS_HPT3XX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
},{ /* 2 */
@@ -1530,7 +1500,7 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
.init_dma = init_dma_hpt366,
.enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
.extra = 240,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAGS_HPT3XX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
},{ /* 3 */
@@ -1540,7 +1510,7 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
.init_dma = init_dma_hpt366,
.enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
.extra = 240,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAGS_HPT3XX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
},{ /* 4 */
@@ -1551,7 +1521,7 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
.enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
.udma_mask = ATA_UDMA5,
.extra = 240,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAGS_HPT3XX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
},{ /* 5 */
@@ -1561,7 +1531,7 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
.init_dma = init_dma_hpt366,
.enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
.extra = 240,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAGS_HPT3XX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
}
diff --git a/trunk/drivers/ide/pci/it8213.c b/trunk/drivers/ide/pci/it8213.c
index 90b52ed37bfc..2a0f45c4f4c4 100644
--- a/trunk/drivers/ide/pci/it8213.c
+++ b/trunk/drivers/ide/pci/it8213.c
@@ -101,24 +101,11 @@ static void it8213_set_dma_mode(ide_drive_t *drive, const u8 speed)
pci_read_config_byte(dev, 0x54, ®54);
pci_read_config_byte(dev, 0x55, ®55);
- switch(speed) {
- case XFER_UDMA_6:
- case XFER_UDMA_4:
- case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break;
- case XFER_UDMA_5:
- case XFER_UDMA_3:
- case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break;
- case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break;
- break;
- case XFER_MW_DMA_2:
- case XFER_MW_DMA_1:
- case XFER_SW_DMA_2:
- break;
- default:
- return;
- }
-
if (speed >= XFER_UDMA_0) {
+ u8 udma = speed - XFER_UDMA_0;
+
+ u_speed = min_t(u8, 2 - (udma & 1), udma) << (drive->dn * 4);
+
if (!(reg48 & u_flag))
pci_write_config_byte(dev, 0x48, reg48 | u_flag);
if (speed >= XFER_UDMA_5) {
diff --git a/trunk/drivers/ide/pci/pdc202xx_new.c b/trunk/drivers/ide/pci/pdc202xx_new.c
index 2b4f44e45a1a..ef4a99b99d1f 100644
--- a/trunk/drivers/ide/pci/pdc202xx_new.c
+++ b/trunk/drivers/ide/pci/pdc202xx_new.c
@@ -146,7 +146,7 @@ static struct udma_timing {
{ 0x1a, 0x01, 0xcb }, /* UDMA mode 6 */
};
-static void pdcnew_set_mode(ide_drive_t *drive, const u8 speed)
+static void pdcnew_set_dma_mode(ide_drive_t *drive, const u8 speed)
{
ide_hwif_t *hwif = HWIF(drive);
u8 adj = (drive->dn & 1) ? 0x08 : 0x00;
@@ -162,45 +162,18 @@ static void pdcnew_set_mode(ide_drive_t *drive, const u8 speed)
if (max_dma_rate(hwif->pci_dev) == 4) {
u8 mode = speed & 0x07;
- switch (speed) {
- case XFER_UDMA_6:
- case XFER_UDMA_5:
- case XFER_UDMA_4:
- case XFER_UDMA_3:
- case XFER_UDMA_2:
- case XFER_UDMA_1:
- case XFER_UDMA_0:
- set_indexed_reg(hwif, 0x10 + adj,
- udma_timings[mode].reg10);
- set_indexed_reg(hwif, 0x11 + adj,
- udma_timings[mode].reg11);
- set_indexed_reg(hwif, 0x12 + adj,
- udma_timings[mode].reg12);
- break;
-
- case XFER_MW_DMA_2:
- case XFER_MW_DMA_1:
- case XFER_MW_DMA_0:
- set_indexed_reg(hwif, 0x0e + adj,
- mwdma_timings[mode].reg0e);
- set_indexed_reg(hwif, 0x0f + adj,
- mwdma_timings[mode].reg0f);
- break;
- case XFER_PIO_4:
- case XFER_PIO_3:
- case XFER_PIO_2:
- case XFER_PIO_1:
- case XFER_PIO_0:
- set_indexed_reg(hwif, 0x0c + adj,
- pio_timings[mode].reg0c);
- set_indexed_reg(hwif, 0x0d + adj,
- pio_timings[mode].reg0d);
- set_indexed_reg(hwif, 0x13 + adj,
- pio_timings[mode].reg13);
- break;
- default:
- printk(KERN_ERR "pdc202xx_new: "
- "Unknown speed %d ignored\n", speed);
+ if (speed >= XFER_UDMA_0) {
+ set_indexed_reg(hwif, 0x10 + adj,
+ udma_timings[mode].reg10);
+ set_indexed_reg(hwif, 0x11 + adj,
+ udma_timings[mode].reg11);
+ set_indexed_reg(hwif, 0x12 + adj,
+ udma_timings[mode].reg12);
+ } else {
+ set_indexed_reg(hwif, 0x0e + adj,
+ mwdma_timings[mode].reg0e);
+ set_indexed_reg(hwif, 0x0f + adj,
+ mwdma_timings[mode].reg0f);
}
} else if (speed == XFER_UDMA_2) {
/* Set tHOLD bit to 0 if using UDMA mode 2 */
@@ -212,7 +185,14 @@ static void pdcnew_set_mode(ide_drive_t *drive, const u8 speed)
static void pdcnew_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
- pdcnew_set_mode(drive, XFER_PIO_0 + pio);
+ ide_hwif_t *hwif = drive->hwif;
+ u8 adj = (drive->dn & 1) ? 0x08 : 0x00;
+
+ if (max_dma_rate(hwif->pci_dev) == 4) {
+ set_indexed_reg(hwif, 0x0c + adj, pio_timings[pio].reg0c);
+ set_indexed_reg(hwif, 0x0d + adj, pio_timings[pio].reg0d);
+ set_indexed_reg(hwif, 0x13 + adj, pio_timings[pio].reg13);
+ }
}
static u8 pdcnew_cable_detect(ide_hwif_t *hwif)
@@ -466,7 +446,7 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha
static void __devinit init_hwif_pdc202new(ide_hwif_t *hwif)
{
hwif->set_pio_mode = &pdcnew_set_pio_mode;
- hwif->set_dma_mode = &pdcnew_set_mode;
+ hwif->set_dma_mode = &pdcnew_set_dma_mode;
hwif->quirkproc = &pdcnew_quirkproc;
hwif->resetproc = &pdcnew_reset;
diff --git a/trunk/drivers/ide/pci/pdc202xx_old.c b/trunk/drivers/ide/pci/pdc202xx_old.c
index e09742e2ba59..67b2781e2213 100644
--- a/trunk/drivers/ide/pci/pdc202xx_old.c
+++ b/trunk/drivers/ide/pci/pdc202xx_old.c
@@ -162,7 +162,7 @@ static u8 pdc202xx_old_cable_detect (ide_hwif_t *hwif)
*/
static void pdc_old_enable_66MHz_clock(ide_hwif_t *hwif)
{
- unsigned long clock_reg = hwif->dma_master + 0x11;
+ unsigned long clock_reg = hwif->extra_base + 0x01;
u8 clock = inb(clock_reg);
outb(clock | (hwif->channel ? 0x08 : 0x02), clock_reg);
@@ -170,7 +170,7 @@ static void pdc_old_enable_66MHz_clock(ide_hwif_t *hwif)
static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif)
{
- unsigned long clock_reg = hwif->dma_master + 0x11;
+ unsigned long clock_reg = hwif->extra_base + 0x01;
u8 clock = inb(clock_reg);
outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg);
@@ -193,7 +193,7 @@ static void pdc202xx_old_ide_dma_start(ide_drive_t *drive)
if (drive->media != ide_disk || drive->addressing == 1) {
struct request *rq = HWGROUP(drive)->rq;
ide_hwif_t *hwif = HWIF(drive);
- unsigned long high_16 = hwif->dma_master;
+ unsigned long high_16 = hwif->extra_base - 16;
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
u32 word_count = 0;
u8 clock = inb(high_16 + 0x11);
@@ -212,7 +212,7 @@ static int pdc202xx_old_ide_dma_end(ide_drive_t *drive)
{
if (drive->media != ide_disk || drive->addressing == 1) {
ide_hwif_t *hwif = HWIF(drive);
- unsigned long high_16 = hwif->dma_master;
+ unsigned long high_16 = hwif->extra_base - 16;
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
u8 clock = 0;
@@ -228,7 +228,7 @@ static int pdc202xx_old_ide_dma_end(ide_drive_t *drive)
static int pdc202xx_old_ide_dma_test_irq(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
- unsigned long high_16 = hwif->dma_master;
+ unsigned long high_16 = hwif->extra_base - 16;
u8 dma_stat = inb(hwif->dma_status);
u8 sc1d = inb(high_16 + 0x001d);
@@ -271,7 +271,7 @@ static void pdc202xx_dma_timeout(ide_drive_t *drive)
static void pdc202xx_reset_host (ide_hwif_t *hwif)
{
- unsigned long high_16 = hwif->dma_master;
+ unsigned long high_16 = hwif->extra_base - 16;
u8 udma_speed_flag = inb(high_16 | 0x001f);
outb(udma_speed_flag | 0x10, high_16 | 0x001f);
@@ -375,6 +375,11 @@ static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev,
}
}
+#define IDE_HFLAGS_PDC202XX \
+ (IDE_HFLAG_ERROR_STOPS_FIFO | \
+ IDE_HFLAG_ABUSE_SET_DMA_MODE | \
+ IDE_HFLAG_OFF_BOARD)
+
#define DECLARE_PDC2026X_DEV(name_str, udma, extra_flags) \
{ \
.name = name_str, \
@@ -382,9 +387,7 @@ static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev,
.init_hwif = init_hwif_pdc202xx, \
.init_dma = init_dma_pdc202xx, \
.extra = 48, \
- .host_flags = IDE_HFLAG_ERROR_STOPS_FIFO | \
- extra_flags | \
- IDE_HFLAG_OFF_BOARD, \
+ .host_flags = IDE_HFLAGS_PDC202XX | extra_flags, \
.pio_mask = ATA_PIO4, \
.mwdma_mask = ATA_MWDMA2, \
.udma_mask = udma, \
@@ -397,8 +400,7 @@ static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
.init_hwif = init_hwif_pdc202xx,
.init_dma = init_dma_pdc202xx,
.extra = 16,
- .host_flags = IDE_HFLAG_ERROR_STOPS_FIFO |
- IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAGS_PDC202XX,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA2,
diff --git a/trunk/drivers/ide/pci/piix.c b/trunk/drivers/ide/pci/piix.c
index 27781d294cea..bd6d3f77d30c 100644
--- a/trunk/drivers/ide/pci/piix.c
+++ b/trunk/drivers/ide/pci/piix.c
@@ -203,20 +203,11 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed)
pci_read_config_byte(dev, 0x54, ®54);
pci_read_config_byte(dev, 0x55, ®55);
- switch(speed) {
- case XFER_UDMA_4:
- case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break;
- case XFER_UDMA_5:
- case XFER_UDMA_3:
- case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break;
- case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break;
- case XFER_MW_DMA_2:
- case XFER_MW_DMA_1:
- case XFER_SW_DMA_2: break;
- default: return;
- }
-
if (speed >= XFER_UDMA_0) {
+ u8 udma = speed - XFER_UDMA_0;
+
+ u_speed = min_t(u8, 2 - (udma & 1), udma) << (drive->dn * 4);
+
if (!(reg48 & u_flag))
pci_write_config_byte(dev, 0x48, reg48 | u_flag);
if (speed == XFER_UDMA_5) {
diff --git a/trunk/drivers/ide/pci/sc1200.c b/trunk/drivers/ide/pci/sc1200.c
index 707d5ff66b03..fef20bd4aa78 100644
--- a/trunk/drivers/ide/pci/sc1200.c
+++ b/trunk/drivers/ide/pci/sc1200.c
@@ -135,59 +135,29 @@ static void sc1200_set_dma_mode(ide_drive_t *drive, const u8 mode)
unsigned short pci_clock;
unsigned int basereg = hwif->channel ? 0x50 : 0x40;
+ static const u32 udma_timing[3][3] = {
+ { 0x00921250, 0x00911140, 0x00911030 },
+ { 0x00932470, 0x00922260, 0x00922140 },
+ { 0x009436a1, 0x00933481, 0x00923261 },
+ };
+
+ static const u32 mwdma_timing[3][3] = {
+ { 0x00077771, 0x00012121, 0x00002020 },
+ { 0x000bbbb2, 0x00024241, 0x00013131 },
+ { 0x000ffff3, 0x00035352, 0x00015151 },
+ };
+
pci_clock = sc1200_get_pci_clock();
/*
* Note that each DMA mode has several timings associated with it.
* The correct timing depends on the fast PCI clock freq.
*/
- timings = 0;
- switch (mode) {
- case XFER_UDMA_0:
- switch (pci_clock) {
- case PCI_CLK_33: timings = 0x00921250; break;
- case PCI_CLK_48: timings = 0x00932470; break;
- case PCI_CLK_66: timings = 0x009436a1; break;
- }
- break;
- case XFER_UDMA_1:
- switch (pci_clock) {
- case PCI_CLK_33: timings = 0x00911140; break;
- case PCI_CLK_48: timings = 0x00922260; break;
- case PCI_CLK_66: timings = 0x00933481; break;
- }
- break;
- case XFER_UDMA_2:
- switch (pci_clock) {
- case PCI_CLK_33: timings = 0x00911030; break;
- case PCI_CLK_48: timings = 0x00922140; break;
- case PCI_CLK_66: timings = 0x00923261; break;
- }
- break;
- case XFER_MW_DMA_0:
- switch (pci_clock) {
- case PCI_CLK_33: timings = 0x00077771; break;
- case PCI_CLK_48: timings = 0x000bbbb2; break;
- case PCI_CLK_66: timings = 0x000ffff3; break;
- }
- break;
- case XFER_MW_DMA_1:
- switch (pci_clock) {
- case PCI_CLK_33: timings = 0x00012121; break;
- case PCI_CLK_48: timings = 0x00024241; break;
- case PCI_CLK_66: timings = 0x00035352; break;
- }
- break;
- case XFER_MW_DMA_2:
- switch (pci_clock) {
- case PCI_CLK_33: timings = 0x00002020; break;
- case PCI_CLK_48: timings = 0x00013131; break;
- case PCI_CLK_66: timings = 0x00015151; break;
- }
- break;
- default:
- return;
- }
+
+ if (mode >= XFER_UDMA_0)
+ timings = udma_timing[pci_clock][mode - XFER_UDMA_0];
+ else
+ timings = mwdma_timing[pci_clock][mode - XFER_MW_DMA_0];
if (unit == 0) { /* are we configuring drive0? */
pci_read_config_dword(hwif->pci_dev, basereg+4, ®);
@@ -260,66 +230,39 @@ static void sc1200_set_pio_mode(ide_drive_t *drive, const u8 pio)
}
#ifdef CONFIG_PM
-static ide_hwif_t *lookup_pci_dev (ide_hwif_t *prev, struct pci_dev *dev)
-{
- int h;
-
- for (h = 0; h < MAX_HWIFS; h++) {
- ide_hwif_t *hwif = &ide_hwifs[h];
- if (prev) {
- if (hwif == prev)
- prev = NULL; // found previous, now look for next match
- } else {
- if (hwif && hwif->pci_dev == dev)
- return hwif; // found next match
- }
- }
- return NULL; // not found
-}
-
-typedef struct sc1200_saved_state_s {
- __u32 regs[4];
-} sc1200_saved_state_t;
-
+struct sc1200_saved_state {
+ u32 regs[8];
+};
static int sc1200_suspend (struct pci_dev *dev, pm_message_t state)
{
- ide_hwif_t *hwif = NULL;
-
printk("SC1200: suspend(%u)\n", state.event);
+ /*
+ * we only save state when going from full power to less
+ */
if (state.event == PM_EVENT_ON) {
- // we only save state when going from full power to less
-
- //
- // Loop over all interfaces that are part of this PCI device:
- //
- while ((hwif = lookup_pci_dev(hwif, dev)) != NULL) {
- sc1200_saved_state_t *ss;
- unsigned int basereg, r;
- //
- // allocate a permanent save area, if not already allocated
- //
- ss = (sc1200_saved_state_t *)hwif->config_data;
- if (ss == NULL) {
- ss = kmalloc(sizeof(sc1200_saved_state_t), GFP_KERNEL);
- if (ss == NULL)
- return -ENOMEM;
- hwif->config_data = (unsigned long)ss;
- }
- ss = (sc1200_saved_state_t *)hwif->config_data;
- //
- // Save timing registers: this may be unnecessary if
- // BIOS also does it
- //
- basereg = hwif->channel ? 0x50 : 0x40;
- for (r = 0; r < 4; ++r) {
- pci_read_config_dword (hwif->pci_dev, basereg + (r<<2), &ss->regs[r]);
- }
+ struct sc1200_saved_state *ss;
+ unsigned int r;
+
+ /*
+ * allocate a permanent save area, if not already allocated
+ */
+ ss = (struct sc1200_saved_state *)pci_get_drvdata(dev);
+ if (ss == NULL) {
+ ss = kmalloc(sizeof(*ss), GFP_KERNEL);
+ if (ss == NULL)
+ return -ENOMEM;
+ pci_set_drvdata(dev, ss);
}
- }
- /* You don't need to iterate over disks -- sysfs should have done that for you already */
+ /*
+ * save timing registers
+ * (this may be unnecessary if BIOS also does it)
+ */
+ for (r = 0; r < 8; r++)
+ pci_read_config_dword(dev, 0x40 + r * 4, &ss->regs[r]);
+ }
pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state));
@@ -328,30 +271,25 @@ static int sc1200_suspend (struct pci_dev *dev, pm_message_t state)
static int sc1200_resume (struct pci_dev *dev)
{
- ide_hwif_t *hwif = NULL;
- int i;
+ struct sc1200_saved_state *ss;
+ unsigned int r;
+ int i;
i = pci_enable_device(dev);
if (i)
return i;
- //
- // loop over all interfaces that are part of this pci device:
- //
- while ((hwif = lookup_pci_dev(hwif, dev)) != NULL) {
- unsigned int basereg, r;
- sc1200_saved_state_t *ss = (sc1200_saved_state_t *)hwif->config_data;
-
- //
- // Restore timing registers: this may be unnecessary if BIOS also does it
- //
- basereg = hwif->channel ? 0x50 : 0x40;
- if (ss != NULL) {
- for (r = 0; r < 4; ++r) {
- pci_write_config_dword(hwif->pci_dev, basereg + (r<<2), ss->regs[r]);
- }
- }
+ ss = (struct sc1200_saved_state *)pci_get_drvdata(dev);
+
+ /*
+ * restore timing registers
+ * (this may be unnecessary if BIOS also does it)
+ */
+ if (ss) {
+ for (r = 0; r < 8; r++)
+ pci_write_config_dword(dev, 0x40 + r * 4, ss->regs[r]);
}
+
return 0;
}
#endif
diff --git a/trunk/drivers/ide/pci/scc_pata.c b/trunk/drivers/ide/pci/scc_pata.c
index ebb7132b9b84..24a85bbcd2a6 100644
--- a/trunk/drivers/ide/pci/scc_pata.c
+++ b/trunk/drivers/ide/pci/scc_pata.c
@@ -254,19 +254,7 @@ static void scc_set_dma_mode(ide_drive_t *drive, const u8 speed)
offset = 0; /* 100MHz */
}
- switch (speed) {
- case XFER_UDMA_6:
- case XFER_UDMA_5:
- case XFER_UDMA_4:
- case XFER_UDMA_3:
- case XFER_UDMA_2:
- case XFER_UDMA_1:
- case XFER_UDMA_0:
- idx = speed - XFER_UDMA_0;
- break;
- default:
- return;
- }
+ idx = speed - XFER_UDMA_0;
jcactsel = JCACTSELtbl[offset][idx];
if (is_slave) {
diff --git a/trunk/drivers/ide/pci/serverworks.c b/trunk/drivers/ide/pci/serverworks.c
index a7280311357b..e9bd269547bb 100644
--- a/trunk/drivers/ide/pci/serverworks.c
+++ b/trunk/drivers/ide/pci/serverworks.c
@@ -366,12 +366,17 @@ static void __devinit init_hwif_svwks (ide_hwif_t *hwif)
}
}
+#define IDE_HFLAGS_SVWKS \
+ (IDE_HFLAG_LEGACY_IRQS | \
+ IDE_HFLAG_ABUSE_SET_DMA_MODE | \
+ IDE_HFLAG_BOOTABLE)
+
static const struct ide_port_info serverworks_chipsets[] __devinitdata = {
{ /* 0 */
.name = "SvrWks OSB4",
.init_chipset = init_chipset_svwks,
.init_hwif = init_hwif_svwks,
- .host_flags = IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_BOOTABLE,
+ .host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = 0x00, /* UDMA is problematic on OSB4 */
@@ -379,7 +384,7 @@ static const struct ide_port_info serverworks_chipsets[] __devinitdata = {
.name = "SvrWks CSB5",
.init_chipset = init_chipset_svwks,
.init_hwif = init_hwif_svwks,
- .host_flags = IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_BOOTABLE,
+ .host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -387,7 +392,7 @@ static const struct ide_port_info serverworks_chipsets[] __devinitdata = {
.name = "SvrWks CSB6",
.init_chipset = init_chipset_svwks,
.init_hwif = init_hwif_svwks,
- .host_flags = IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_BOOTABLE,
+ .host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -395,8 +400,7 @@ static const struct ide_port_info serverworks_chipsets[] __devinitdata = {
.name = "SvrWks CSB6",
.init_chipset = init_chipset_svwks,
.init_hwif = init_hwif_svwks,
- .host_flags = IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_SINGLE |
- IDE_HFLAG_BOOTABLE,
+ .host_flags = IDE_HFLAGS_SVWKS | IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -404,8 +408,7 @@ static const struct ide_port_info serverworks_chipsets[] __devinitdata = {
.name = "SvrWks HT1000",
.init_chipset = init_chipset_svwks,
.init_hwif = init_hwif_svwks,
- .host_flags = IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_SINGLE |
- IDE_HFLAG_BOOTABLE,
+ .host_flags = IDE_HFLAGS_SVWKS | IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
diff --git a/trunk/drivers/ide/pci/sgiioc4.c b/trunk/drivers/ide/pci/sgiioc4.c
index de820aa58cd0..7e9dade5648d 100644
--- a/trunk/drivers/ide/pci/sgiioc4.c
+++ b/trunk/drivers/ide/pci/sgiioc4.c
@@ -582,7 +582,6 @@ ide_init_sgiioc4(ide_hwif_t * hwif)
hwif->pre_reset = NULL; /* No HBA specific pre_set needed */
hwif->resetproc = &sgiioc4_resetproc;/* Reset DMA engine,
clear interrupts */
- hwif->intrproc = NULL; /* Enable or Disable interrupt from drive */
hwif->maskproc = &sgiioc4_maskproc; /* Mask on/off NIEN register */
hwif->quirkproc = NULL;
hwif->busproc = NULL;
diff --git a/trunk/drivers/ide/pci/siimage.c b/trunk/drivers/ide/pci/siimage.c
index 5709c252543b..7b45eaf5afd9 100644
--- a/trunk/drivers/ide/pci/siimage.c
+++ b/trunk/drivers/ide/pci/siimage.c
@@ -278,27 +278,14 @@ static void sil_set_dma_mode(ide_drive_t *drive, const u8 speed)
scsc = is_sata(hwif) ? 1 : scsc;
- switch(speed) {
- case XFER_MW_DMA_2:
- case XFER_MW_DMA_1:
- case XFER_MW_DMA_0:
- multi = dma[speed - XFER_MW_DMA_0];
- mode |= ((unit) ? 0x20 : 0x02);
- break;
- case XFER_UDMA_6:
- case XFER_UDMA_5:
- case XFER_UDMA_4:
- case XFER_UDMA_3:
- case XFER_UDMA_2:
- case XFER_UDMA_1:
- case XFER_UDMA_0:
- multi = dma[2];
- ultra |= ((scsc) ? (ultra6[speed - XFER_UDMA_0]) :
- (ultra5[speed - XFER_UDMA_0]));
- mode |= ((unit) ? 0x30 : 0x03);
- break;
- default:
- return;
+ if (speed >= XFER_UDMA_0) {
+ multi = dma[2];
+ ultra |= (scsc ? ultra6[speed - XFER_UDMA_0] :
+ ultra5[speed - XFER_UDMA_0]);
+ mode |= (unit ? 0x30 : 0x03);
+ } else {
+ multi = dma[speed - XFER_MW_DMA_0];
+ mode |= (unit ? 0x20 : 0x02);
}
if (hwif->mmio) {
diff --git a/trunk/drivers/ide/pci/sis5513.c b/trunk/drivers/ide/pci/sis5513.c
index d90b42917775..85d36996e6af 100644
--- a/trunk/drivers/ide/pci/sis5513.c
+++ b/trunk/drivers/ide/pci/sis5513.c
@@ -305,59 +305,56 @@ static void sis_set_pio_mode(ide_drive_t *drive, const u8 pio)
sis_program_timings(drive, XFER_PIO_0 + pio);
}
-static void sis_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void sis_ata133_program_udma_timings(ide_drive_t *drive, const u8 mode)
{
- ide_hwif_t *hwif = HWIF(drive);
- struct pci_dev *dev = hwif->pci_dev;
+ struct pci_dev *dev = drive->hwif->pci_dev;
+ u32 regdw = 0;
+ u8 drive_pci = sis_ata133_get_base(drive), clk, idx;
- /* Config chip for mode */
- switch(speed) {
- case XFER_UDMA_6:
- case XFER_UDMA_5:
- case XFER_UDMA_4:
- case XFER_UDMA_3:
- case XFER_UDMA_2:
- case XFER_UDMA_1:
- case XFER_UDMA_0:
- if (chipset_family >= ATA_133) {
- u32 regdw = 0;
- u8 drive_pci = sis_ata133_get_base(drive);
-
- pci_read_config_dword(dev, drive_pci, ®dw);
- regdw |= 0x04;
- regdw &= 0xfffff00f;
- /* check if ATA133 enable */
- if (regdw & 0x08) {
- regdw |= (unsigned long)cycle_time_value[ATA_133][speed-XFER_UDMA_0] << 4;
- regdw |= (unsigned long)cvs_time_value[ATA_133][speed-XFER_UDMA_0] << 8;
- } else {
- regdw |= (unsigned long)cycle_time_value[ATA_100][speed-XFER_UDMA_0] << 4;
- regdw |= (unsigned long)cvs_time_value[ATA_100][speed-XFER_UDMA_0] << 8;
- }
- pci_write_config_dword(dev, (unsigned long)drive_pci, regdw);
- } else {
- u8 drive_pci = 0x40 + drive->dn * 2, reg = 0;
-
- pci_read_config_byte(dev, drive_pci+1, ®);
- /* Force the UDMA bit on if we want to use UDMA */
- reg |= 0x80;
- /* clean reg cycle time bits */
- reg &= ~((0xFF >> (8 - cycle_time_range[chipset_family]))
- << cycle_time_offset[chipset_family]);
- /* set reg cycle time bits */
- reg |= cycle_time_value[chipset_family][speed-XFER_UDMA_0]
- << cycle_time_offset[chipset_family];
- pci_write_config_byte(dev, drive_pci+1, reg);
- }
- break;
- case XFER_MW_DMA_2:
- case XFER_MW_DMA_1:
- case XFER_MW_DMA_0:
- sis_program_timings(drive, speed);
- break;
- default:
- break;
- }
+ pci_read_config_dword(dev, drive_pci, ®dw);
+
+ regdw |= 0x04;
+ regdw &= 0xfffff00f;
+ /* check if ATA133 enable */
+ clk = (regdw & 0x08) ? ATA_133 : ATA_100;
+ idx = mode - XFER_UDMA_0;
+ regdw |= cycle_time_value[clk][idx] << 4;
+ regdw |= cvs_time_value[clk][idx] << 8;
+
+ pci_write_config_dword(dev, drive_pci, regdw);
+}
+
+static void sis_ata33_program_udma_timings(ide_drive_t *drive, const u8 mode)
+{
+ struct pci_dev *dev = drive->hwif->pci_dev;
+ u8 drive_pci = 0x40 + drive->dn * 2, reg = 0, i = chipset_family;
+
+ pci_read_config_byte(dev, drive_pci + 1, ®);
+
+ /* force the UDMA bit on if we want to use UDMA */
+ reg |= 0x80;
+ /* clean reg cycle time bits */
+ reg &= ~((0xff >> (8 - cycle_time_range[i])) << cycle_time_offset[i]);
+ /* set reg cycle time bits */
+ reg |= cycle_time_value[i][mode - XFER_UDMA_0] << cycle_time_offset[i];
+
+ pci_write_config_byte(dev, drive_pci + 1, reg);
+}
+
+static void sis_program_udma_timings(ide_drive_t *drive, const u8 mode)
+{
+ if (chipset_family >= ATA_133) /* ATA_133 */
+ sis_ata133_program_udma_timings(drive, mode);
+ else /* ATA_33/66/100a/100/133a */
+ sis_ata33_program_udma_timings(drive, mode);
+}
+
+static void sis_set_dma_mode(ide_drive_t *drive, const u8 speed)
+{
+ if (speed >= XFER_UDMA_0)
+ sis_program_udma_timings(drive, speed);
+ else
+ sis_program_timings(drive, speed);
}
static u8 sis5513_ata133_udma_filter(ide_drive_t *drive)
diff --git a/trunk/drivers/ide/pci/sl82c105.c b/trunk/drivers/ide/pci/sl82c105.c
index 147d783f7529..069f104fdcea 100644
--- a/trunk/drivers/ide/pci/sl82c105.c
+++ b/trunk/drivers/ide/pci/sl82c105.c
@@ -115,32 +115,24 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
DBG(("sl82c105_tune_chipset(drive:%s, speed:%s)\n",
drive->name, ide_xfer_verbose(speed)));
- switch (speed) {
- case XFER_MW_DMA_2:
- case XFER_MW_DMA_1:
- case XFER_MW_DMA_0:
- drv_ctrl = mwdma_timings[speed - XFER_MW_DMA_0];
+ drv_ctrl = mwdma_timings[speed - XFER_MW_DMA_0];
- /*
- * Store the DMA timings so that we can actually program
- * them when DMA will be turned on...
- */
- drive->drive_data &= 0x0000ffff;
- drive->drive_data |= (unsigned long)drv_ctrl << 16;
+ /*
+ * Store the DMA timings so that we can actually program
+ * them when DMA will be turned on...
+ */
+ drive->drive_data &= 0x0000ffff;
+ drive->drive_data |= (unsigned long)drv_ctrl << 16;
- /*
- * If we are already using DMA, we just reprogram
- * the drive control register.
- */
- if (drive->using_dma) {
- struct pci_dev *dev = HWIF(drive)->pci_dev;
- int reg = 0x44 + drive->dn * 4;
-
- pci_write_config_word(dev, reg, drv_ctrl);
- }
- break;
- default:
- return;
+ /*
+ * If we are already using DMA, we just reprogram
+ * the drive control register.
+ */
+ if (drive->using_dma) {
+ struct pci_dev *dev = HWIF(drive)->pci_dev;
+ int reg = 0x44 + drive->dn * 4;
+
+ pci_write_config_word(dev, reg, drv_ctrl);
}
}
diff --git a/trunk/drivers/ide/pci/slc90e66.c b/trunk/drivers/ide/pci/slc90e66.c
index eb4445b229ed..dbbb46819a2d 100644
--- a/trunk/drivers/ide/pci/slc90e66.c
+++ b/trunk/drivers/ide/pci/slc90e66.c
@@ -91,19 +91,9 @@ static void slc90e66_set_dma_mode(ide_drive_t *drive, const u8 speed)
pci_read_config_word(dev, 0x48, ®48);
pci_read_config_word(dev, 0x4a, ®4a);
- switch(speed) {
- case XFER_UDMA_4: u_speed = 4 << (drive->dn * 4); break;
- case XFER_UDMA_3: u_speed = 3 << (drive->dn * 4); break;
- case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break;
- case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break;
- case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break;
- case XFER_MW_DMA_2:
- case XFER_MW_DMA_1:
- case XFER_SW_DMA_2: break;
- default: return;
- }
-
if (speed >= XFER_UDMA_0) {
+ u_speed = (speed - XFER_UDMA_0) << (drive->dn * 4);
+
if (!(reg48 & u_flag))
pci_write_config_word(dev, 0x48, reg48|u_flag);
/* FIXME: (reg4a & a_speed) ? */
diff --git a/trunk/drivers/ide/pci/tc86c001.c b/trunk/drivers/ide/pci/tc86c001.c
index a66ebd14664e..e1faf6c2fe16 100644
--- a/trunk/drivers/ide/pci/tc86c001.c
+++ b/trunk/drivers/ide/pci/tc86c001.c
@@ -222,7 +222,8 @@ static const struct ide_port_info tc86c001_chipset __devinitdata = {
.name = "TC86C001",
.init_chipset = init_chipset_tc86c001,
.init_hwif = init_hwif_tc86c001,
- .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD,
+ .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD |
+ IDE_HFLAG_ABUSE_SET_DMA_MODE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA4,
diff --git a/trunk/drivers/ide/pci/triflex.c b/trunk/drivers/ide/pci/triflex.c
index a227c41d23a3..ae52a96a1cf9 100644
--- a/trunk/drivers/ide/pci/triflex.c
+++ b/trunk/drivers/ide/pci/triflex.c
@@ -81,8 +81,6 @@ static void triflex_set_mode(ide_drive_t *drive, const u8 speed)
case XFER_PIO_0:
timing = 0x0808;
break;
- default:
- return;
}
triflex_timings &= ~(0xFFFF << (16 * unit));
diff --git a/trunk/drivers/ide/pci/via82cxxx.c b/trunk/drivers/ide/pci/via82cxxx.c
index a0d3c16b68ec..4b32c90f4896 100644
--- a/trunk/drivers/ide/pci/via82cxxx.c
+++ b/trunk/drivers/ide/pci/via82cxxx.c
@@ -439,6 +439,7 @@ static const struct ide_port_info via82cxxx_chipset __devinitdata = {
.enablebits = { { 0x40, 0x02, 0x02 }, { 0x40, 0x01, 0x01 } },
.host_flags = IDE_HFLAG_PIO_NO_BLACKLIST |
IDE_HFLAG_PIO_NO_DOWNGRADE |
+ IDE_HFLAG_ABUSE_SET_DMA_MODE |
IDE_HFLAG_POST_SET_MODE |
IDE_HFLAG_IO_32BIT |
IDE_HFLAG_BOOTABLE,
diff --git a/trunk/drivers/ide/ppc/pmac.c b/trunk/drivers/ide/ppc/pmac.c
index 7f7a59885777..3dce80092fff 100644
--- a/trunk/drivers/ide/ppc/pmac.c
+++ b/trunk/drivers/ide/ppc/pmac.c
@@ -438,13 +438,8 @@ pmac_ide_init_hwif_ports(hw_regs_t *hw,
if (data_port == pmac_ide[ix].regbase)
break;
- if (ix >= MAX_HWIFS) {
- /* Probably a PCI interface... */
- for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i)
- hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET;
- hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
- return;
- }
+ if (ix >= MAX_HWIFS)
+ return; /* not an IDE PMAC interface */
for (i = 0; i < 8; ++i)
hw->io_ports[i] = data_port + i * 0x10;
@@ -833,38 +828,20 @@ static void pmac_ide_set_dma_mode(ide_drive_t *drive, const u8 speed)
tl[0] = *timings;
tl[1] = *timings2;
- switch(speed) {
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
- case XFER_UDMA_6:
- case XFER_UDMA_5:
- case XFER_UDMA_4:
- case XFER_UDMA_3:
- case XFER_UDMA_2:
- case XFER_UDMA_1:
- case XFER_UDMA_0:
- if (pmif->kind == controller_kl_ata4)
- ret = set_timings_udma_ata4(&tl[0], speed);
- else if (pmif->kind == controller_un_ata6
- || pmif->kind == controller_k2_ata6)
- ret = set_timings_udma_ata6(&tl[0], &tl[1], speed);
- else if (pmif->kind == controller_sh_ata6)
- ret = set_timings_udma_shasta(&tl[0], &tl[1], speed);
- else
- ret = 1;
- break;
- case XFER_MW_DMA_2:
- case XFER_MW_DMA_1:
- case XFER_MW_DMA_0:
- set_timings_mdma(drive, pmif->kind, &tl[0], &tl[1], speed);
- break;
- case XFER_SW_DMA_2:
- case XFER_SW_DMA_1:
- case XFER_SW_DMA_0:
- return;
+ if (speed >= XFER_UDMA_0) {
+ if (pmif->kind == controller_kl_ata4)
+ ret = set_timings_udma_ata4(&tl[0], speed);
+ else if (pmif->kind == controller_un_ata6
+ || pmif->kind == controller_k2_ata6)
+ ret = set_timings_udma_ata6(&tl[0], &tl[1], speed);
+ else if (pmif->kind == controller_sh_ata6)
+ ret = set_timings_udma_shasta(&tl[0], &tl[1], speed);
+ else
+ ret = -1;
+ } else
+ set_timings_mdma(drive, pmif->kind, &tl[0], &tl[1], speed);
#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
- default:
- ret = 1;
- }
if (ret)
return;
diff --git a/trunk/drivers/infiniband/core/cm.c b/trunk/drivers/infiniband/core/cm.c
index 2e39236d189f..c0150147d347 100644
--- a/trunk/drivers/infiniband/core/cm.c
+++ b/trunk/drivers/infiniband/core/cm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2006 Intel Corporation. All rights reserved.
+ * Copyright (c) 2004-2007 Intel Corporation. All rights reserved.
* Copyright (c) 2004 Topspin Corporation. All rights reserved.
* Copyright (c) 2004, 2005 Voltaire Corporation. All rights reserved.
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
@@ -37,12 +37,14 @@
#include
#include
+#include
#include
#include
#include
#include
#include
#include
+#include
#include
#include
@@ -78,17 +80,94 @@ static struct ib_cm {
struct workqueue_struct *wq;
} cm;
+/* Counter indexes ordered by attribute ID */
+enum {
+ CM_REQ_COUNTER,
+ CM_MRA_COUNTER,
+ CM_REJ_COUNTER,
+ CM_REP_COUNTER,
+ CM_RTU_COUNTER,
+ CM_DREQ_COUNTER,
+ CM_DREP_COUNTER,
+ CM_SIDR_REQ_COUNTER,
+ CM_SIDR_REP_COUNTER,
+ CM_LAP_COUNTER,
+ CM_APR_COUNTER,
+ CM_ATTR_COUNT,
+ CM_ATTR_ID_OFFSET = 0x0010,
+};
+
+enum {
+ CM_XMIT,
+ CM_XMIT_RETRIES,
+ CM_RECV,
+ CM_RECV_DUPLICATES,
+ CM_COUNTER_GROUPS
+};
+
+static char const counter_group_names[CM_COUNTER_GROUPS]
+ [sizeof("cm_rx_duplicates")] = {
+ "cm_tx_msgs", "cm_tx_retries",
+ "cm_rx_msgs", "cm_rx_duplicates"
+};
+
+struct cm_counter_group {
+ struct kobject obj;
+ atomic_long_t counter[CM_ATTR_COUNT];
+};
+
+struct cm_counter_attribute {
+ struct attribute attr;
+ int index;
+};
+
+#define CM_COUNTER_ATTR(_name, _index) \
+struct cm_counter_attribute cm_##_name##_counter_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0444, .owner = THIS_MODULE }, \
+ .index = _index \
+}
+
+static CM_COUNTER_ATTR(req, CM_REQ_COUNTER);
+static CM_COUNTER_ATTR(mra, CM_MRA_COUNTER);
+static CM_COUNTER_ATTR(rej, CM_REJ_COUNTER);
+static CM_COUNTER_ATTR(rep, CM_REP_COUNTER);
+static CM_COUNTER_ATTR(rtu, CM_RTU_COUNTER);
+static CM_COUNTER_ATTR(dreq, CM_DREQ_COUNTER);
+static CM_COUNTER_ATTR(drep, CM_DREP_COUNTER);
+static CM_COUNTER_ATTR(sidr_req, CM_SIDR_REQ_COUNTER);
+static CM_COUNTER_ATTR(sidr_rep, CM_SIDR_REP_COUNTER);
+static CM_COUNTER_ATTR(lap, CM_LAP_COUNTER);
+static CM_COUNTER_ATTR(apr, CM_APR_COUNTER);
+
+static struct attribute *cm_counter_default_attrs[] = {
+ &cm_req_counter_attr.attr,
+ &cm_mra_counter_attr.attr,
+ &cm_rej_counter_attr.attr,
+ &cm_rep_counter_attr.attr,
+ &cm_rtu_counter_attr.attr,
+ &cm_dreq_counter_attr.attr,
+ &cm_drep_counter_attr.attr,
+ &cm_sidr_req_counter_attr.attr,
+ &cm_sidr_rep_counter_attr.attr,
+ &cm_lap_counter_attr.attr,
+ &cm_apr_counter_attr.attr,
+ NULL
+};
+
struct cm_port {
struct cm_device *cm_dev;
struct ib_mad_agent *mad_agent;
+ struct kobject port_obj;
u8 port_num;
+ struct cm_counter_group counter_group[CM_COUNTER_GROUPS];
};
struct cm_device {
struct list_head list;
struct ib_device *device;
+ struct kobject dev_obj;
u8 ack_delay;
- struct cm_port port[0];
+ struct cm_port *port[0];
};
struct cm_av {
@@ -278,7 +357,7 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
list_for_each_entry(cm_dev, &cm.device_list, list) {
if (!ib_find_cached_gid(cm_dev->device, &path->sgid,
&p, NULL)) {
- port = &cm_dev->port[p-1];
+ port = cm_dev->port[p-1];
break;
}
}
@@ -1270,6 +1349,9 @@ static void cm_dup_req_handler(struct cm_work *work,
struct ib_mad_send_buf *msg = NULL;
int ret;
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_REQ_COUNTER]);
+
/* Quick state check to discard duplicate REQs. */
if (cm_id_priv->id.state == IB_CM_REQ_RCVD)
return;
@@ -1616,6 +1698,8 @@ static void cm_dup_rep_handler(struct cm_work *work)
if (!cm_id_priv)
return;
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_REP_COUNTER]);
ret = cm_alloc_response_msg(work->port, work->mad_recv_wc, &msg);
if (ret)
goto deref;
@@ -1781,6 +1865,8 @@ static int cm_rtu_handler(struct cm_work *work)
if (cm_id_priv->id.state != IB_CM_REP_SENT &&
cm_id_priv->id.state != IB_CM_MRA_REP_RCVD) {
spin_unlock_irq(&cm_id_priv->lock);
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_RTU_COUNTER]);
goto out;
}
cm_id_priv->id.state = IB_CM_ESTABLISHED;
@@ -1958,6 +2044,8 @@ static int cm_dreq_handler(struct cm_work *work)
cm_id_priv = cm_acquire_id(dreq_msg->remote_comm_id,
dreq_msg->local_comm_id);
if (!cm_id_priv) {
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_DREQ_COUNTER]);
cm_issue_drep(work->port, work->mad_recv_wc);
return -EINVAL;
}
@@ -1977,6 +2065,8 @@ static int cm_dreq_handler(struct cm_work *work)
case IB_CM_MRA_REP_RCVD:
break;
case IB_CM_TIMEWAIT:
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_DREQ_COUNTER]);
if (cm_alloc_response_msg(work->port, work->mad_recv_wc, &msg))
goto unlock;
@@ -1988,6 +2078,10 @@ static int cm_dreq_handler(struct cm_work *work)
if (ib_post_send_mad(msg, NULL))
cm_free_msg(msg);
goto deref;
+ case IB_CM_DREQ_RCVD:
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_DREQ_COUNTER]);
+ goto unlock;
default:
goto unlock;
}
@@ -2339,10 +2433,20 @@ static int cm_mra_handler(struct cm_work *work)
if (cm_mra_get_msg_mraed(mra_msg) != CM_MSG_RESPONSE_OTHER ||
cm_id_priv->id.lap_state != IB_CM_LAP_SENT ||
ib_modify_mad(cm_id_priv->av.port->mad_agent,
- cm_id_priv->msg, timeout))
+ cm_id_priv->msg, timeout)) {
+ if (cm_id_priv->id.lap_state == IB_CM_MRA_LAP_RCVD)
+ atomic_long_inc(&work->port->
+ counter_group[CM_RECV_DUPLICATES].
+ counter[CM_MRA_COUNTER]);
goto out;
+ }
cm_id_priv->id.lap_state = IB_CM_MRA_LAP_RCVD;
break;
+ case IB_CM_MRA_REQ_RCVD:
+ case IB_CM_MRA_REP_RCVD:
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_MRA_COUNTER]);
+ /* fall through */
default:
goto out;
}
@@ -2502,6 +2606,8 @@ static int cm_lap_handler(struct cm_work *work)
case IB_CM_LAP_IDLE:
break;
case IB_CM_MRA_LAP_SENT:
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_LAP_COUNTER]);
if (cm_alloc_response_msg(work->port, work->mad_recv_wc, &msg))
goto unlock;
@@ -2515,6 +2621,10 @@ static int cm_lap_handler(struct cm_work *work)
if (ib_post_send_mad(msg, NULL))
cm_free_msg(msg);
goto deref;
+ case IB_CM_LAP_RCVD:
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_LAP_COUNTER]);
+ goto unlock;
default:
goto unlock;
}
@@ -2796,6 +2906,8 @@ static int cm_sidr_req_handler(struct cm_work *work)
cur_cm_id_priv = cm_insert_remote_sidr(cm_id_priv);
if (cur_cm_id_priv) {
spin_unlock_irq(&cm.lock);
+ atomic_long_inc(&work->port->counter_group[CM_RECV_DUPLICATES].
+ counter[CM_SIDR_REQ_COUNTER]);
goto out; /* Duplicate message. */
}
cm_id_priv->id.state = IB_CM_SIDR_REQ_RCVD;
@@ -2990,6 +3102,27 @@ static void cm_send_handler(struct ib_mad_agent *mad_agent,
struct ib_mad_send_wc *mad_send_wc)
{
struct ib_mad_send_buf *msg = mad_send_wc->send_buf;
+ struct cm_port *port;
+ u16 attr_index;
+
+ port = mad_agent->context;
+ attr_index = be16_to_cpu(((struct ib_mad_hdr *)
+ msg->mad)->attr_id) - CM_ATTR_ID_OFFSET;
+
+ /*
+ * If the send was in response to a received message (context[0] is not
+ * set to a cm_id), and is not a REJ, then it is a send that was
+ * manually retried.
+ */
+ if (!msg->context[0] && (attr_index != CM_REJ_COUNTER))
+ msg->retries = 1;
+
+ atomic_long_add(1 + msg->retries,
+ &port->counter_group[CM_XMIT].counter[attr_index]);
+ if (msg->retries)
+ atomic_long_add(msg->retries,
+ &port->counter_group[CM_XMIT_RETRIES].
+ counter[attr_index]);
switch (mad_send_wc->status) {
case IB_WC_SUCCESS:
@@ -3148,8 +3281,10 @@ EXPORT_SYMBOL(ib_cm_notify);
static void cm_recv_handler(struct ib_mad_agent *mad_agent,
struct ib_mad_recv_wc *mad_recv_wc)
{
+ struct cm_port *port = mad_agent->context;
struct cm_work *work;
enum ib_cm_event_type event;
+ u16 attr_id;
int paths = 0;
switch (mad_recv_wc->recv_buf.mad->mad_hdr.attr_id) {
@@ -3194,6 +3329,10 @@ static void cm_recv_handler(struct ib_mad_agent *mad_agent,
return;
}
+ attr_id = be16_to_cpu(mad_recv_wc->recv_buf.mad->mad_hdr.attr_id);
+ atomic_long_inc(&port->counter_group[CM_RECV].
+ counter[attr_id - CM_ATTR_ID_OFFSET]);
+
work = kmalloc(sizeof *work + sizeof(struct ib_sa_path_rec) * paths,
GFP_KERNEL);
if (!work) {
@@ -3204,7 +3343,7 @@ static void cm_recv_handler(struct ib_mad_agent *mad_agent,
INIT_DELAYED_WORK(&work->work, cm_work_handler);
work->cm_event.event = event;
work->mad_recv_wc = mad_recv_wc;
- work->port = (struct cm_port *)mad_agent->context;
+ work->port = port;
queue_delayed_work(cm.wq, &work->work, 0);
}
@@ -3379,6 +3518,108 @@ static void cm_get_ack_delay(struct cm_device *cm_dev)
cm_dev->ack_delay = attr.local_ca_ack_delay;
}
+static ssize_t cm_show_counter(struct kobject *obj, struct attribute *attr,
+ char *buf)
+{
+ struct cm_counter_group *group;
+ struct cm_counter_attribute *cm_attr;
+
+ group = container_of(obj, struct cm_counter_group, obj);
+ cm_attr = container_of(attr, struct cm_counter_attribute, attr);
+
+ return sprintf(buf, "%ld\n",
+ atomic_long_read(&group->counter[cm_attr->index]));
+}
+
+static struct sysfs_ops cm_counter_ops = {
+ .show = cm_show_counter
+};
+
+static struct kobj_type cm_counter_obj_type = {
+ .sysfs_ops = &cm_counter_ops,
+ .default_attrs = cm_counter_default_attrs
+};
+
+static void cm_release_port_obj(struct kobject *obj)
+{
+ struct cm_port *cm_port;
+
+ printk(KERN_ERR "free cm port\n");
+
+ cm_port = container_of(obj, struct cm_port, port_obj);
+ kfree(cm_port);
+}
+
+static struct kobj_type cm_port_obj_type = {
+ .release = cm_release_port_obj
+};
+
+static void cm_release_dev_obj(struct kobject *obj)
+{
+ struct cm_device *cm_dev;
+
+ printk(KERN_ERR "free cm dev\n");
+
+ cm_dev = container_of(obj, struct cm_device, dev_obj);
+ kfree(cm_dev);
+}
+
+static struct kobj_type cm_dev_obj_type = {
+ .release = cm_release_dev_obj
+};
+
+struct class cm_class = {
+ .name = "infiniband_cm",
+};
+EXPORT_SYMBOL(cm_class);
+
+static void cm_remove_fs_obj(struct kobject *obj)
+{
+ kobject_put(obj->parent);
+ kobject_put(obj);
+}
+
+static int cm_create_port_fs(struct cm_port *port)
+{
+ int i, ret;
+
+ ret = kobject_init_and_add(&port->port_obj, &cm_port_obj_type,
+ kobject_get(&port->cm_dev->dev_obj),
+ "%d", port->port_num);
+ if (ret) {
+ kfree(port);
+ return ret;
+ }
+
+ for (i = 0; i < CM_COUNTER_GROUPS; i++) {
+ ret = kobject_init_and_add(&port->counter_group[i].obj,
+ &cm_counter_obj_type,
+ kobject_get(&port->port_obj),
+ "%s", counter_group_names[i]);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ while (i--)
+ cm_remove_fs_obj(&port->counter_group[i].obj);
+ cm_remove_fs_obj(&port->port_obj);
+ return ret;
+
+}
+
+static void cm_remove_port_fs(struct cm_port *port)
+{
+ int i;
+
+ for (i = 0; i < CM_COUNTER_GROUPS; i++)
+ cm_remove_fs_obj(&port->counter_group[i].obj);
+
+ cm_remove_fs_obj(&port->port_obj);
+}
+
static void cm_add_one(struct ib_device *device)
{
struct cm_device *cm_dev;
@@ -3397,7 +3638,7 @@ static void cm_add_one(struct ib_device *device)
if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
return;
- cm_dev = kmalloc(sizeof(*cm_dev) + sizeof(*port) *
+ cm_dev = kzalloc(sizeof(*cm_dev) + sizeof(*port) *
device->phys_port_cnt, GFP_KERNEL);
if (!cm_dev)
return;
@@ -3405,11 +3646,27 @@ static void cm_add_one(struct ib_device *device)
cm_dev->device = device;
cm_get_ack_delay(cm_dev);
+ ret = kobject_init_and_add(&cm_dev->dev_obj, &cm_dev_obj_type,
+ &cm_class.subsys.kobj, "%s", device->name);
+ if (ret) {
+ kfree(cm_dev);
+ return;
+ }
+
set_bit(IB_MGMT_METHOD_SEND, reg_req.method_mask);
for (i = 1; i <= device->phys_port_cnt; i++) {
- port = &cm_dev->port[i-1];
+ port = kzalloc(sizeof *port, GFP_KERNEL);
+ if (!port)
+ goto error1;
+
+ cm_dev->port[i-1] = port;
port->cm_dev = cm_dev;
port->port_num = i;
+
+ ret = cm_create_port_fs(port);
+ if (ret)
+ goto error1;
+
port->mad_agent = ib_register_mad_agent(device, i,
IB_QPT_GSI,
®_req,
@@ -3418,11 +3675,11 @@ static void cm_add_one(struct ib_device *device)
cm_recv_handler,
port);
if (IS_ERR(port->mad_agent))
- goto error1;
+ goto error2;
ret = ib_modify_port(device, i, 0, &port_modify);
if (ret)
- goto error2;
+ goto error3;
}
ib_set_client_data(device, &cm_client, cm_dev);
@@ -3431,17 +3688,20 @@ static void cm_add_one(struct ib_device *device)
write_unlock_irqrestore(&cm.device_lock, flags);
return;
-error2:
+error3:
ib_unregister_mad_agent(port->mad_agent);
+error2:
+ cm_remove_port_fs(port);
error1:
port_modify.set_port_cap_mask = 0;
port_modify.clr_port_cap_mask = IB_PORT_CM_SUP;
while (--i) {
- port = &cm_dev->port[i-1];
+ port = cm_dev->port[i-1];
ib_modify_port(device, port->port_num, 0, &port_modify);
ib_unregister_mad_agent(port->mad_agent);
+ cm_remove_port_fs(port);
}
- kfree(cm_dev);
+ cm_remove_fs_obj(&cm_dev->dev_obj);
}
static void cm_remove_one(struct ib_device *device)
@@ -3463,11 +3723,12 @@ static void cm_remove_one(struct ib_device *device)
write_unlock_irqrestore(&cm.device_lock, flags);
for (i = 1; i <= device->phys_port_cnt; i++) {
- port = &cm_dev->port[i-1];
+ port = cm_dev->port[i-1];
ib_modify_port(device, port->port_num, 0, &port_modify);
ib_unregister_mad_agent(port->mad_agent);
+ cm_remove_port_fs(port);
}
- kfree(cm_dev);
+ cm_remove_fs_obj(&cm_dev->dev_obj);
}
static int __init ib_cm_init(void)
@@ -3488,17 +3749,25 @@ static int __init ib_cm_init(void)
idr_pre_get(&cm.local_id_table, GFP_KERNEL);
INIT_LIST_HEAD(&cm.timewait_list);
- cm.wq = create_workqueue("ib_cm");
- if (!cm.wq)
+ ret = class_register(&cm_class);
+ if (ret)
return -ENOMEM;
+ cm.wq = create_workqueue("ib_cm");
+ if (!cm.wq) {
+ ret = -ENOMEM;
+ goto error1;
+ }
+
ret = ib_register_client(&cm_client);
if (ret)
- goto error;
+ goto error2;
return 0;
-error:
+error2:
destroy_workqueue(cm.wq);
+error1:
+ class_unregister(&cm_class);
return ret;
}
@@ -3519,6 +3788,7 @@ static void __exit ib_cm_cleanup(void)
}
ib_unregister_client(&cm_client);
+ class_unregister(&cm_class);
idr_destroy(&cm.local_id_table);
}
diff --git a/trunk/drivers/infiniband/core/cma.c b/trunk/drivers/infiniband/core/cma.c
index 0751697ef984..637efead97a0 100644
--- a/trunk/drivers/infiniband/core/cma.c
+++ b/trunk/drivers/infiniband/core/cma.c
@@ -488,7 +488,8 @@ void rdma_destroy_qp(struct rdma_cm_id *id)
}
EXPORT_SYMBOL(rdma_destroy_qp);
-static int cma_modify_qp_rtr(struct rdma_id_private *id_priv)
+static int cma_modify_qp_rtr(struct rdma_id_private *id_priv,
+ struct rdma_conn_param *conn_param)
{
struct ib_qp_attr qp_attr;
int qp_attr_mask, ret;
@@ -514,13 +515,16 @@ static int cma_modify_qp_rtr(struct rdma_id_private *id_priv)
if (ret)
goto out;
+ if (conn_param)
+ qp_attr.max_dest_rd_atomic = conn_param->responder_resources;
ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
out:
mutex_unlock(&id_priv->qp_mutex);
return ret;
}
-static int cma_modify_qp_rts(struct rdma_id_private *id_priv)
+static int cma_modify_qp_rts(struct rdma_id_private *id_priv,
+ struct rdma_conn_param *conn_param)
{
struct ib_qp_attr qp_attr;
int qp_attr_mask, ret;
@@ -536,6 +540,8 @@ static int cma_modify_qp_rts(struct rdma_id_private *id_priv)
if (ret)
goto out;
+ if (conn_param)
+ qp_attr.max_rd_atomic = conn_param->initiator_depth;
ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
out:
mutex_unlock(&id_priv->qp_mutex);
@@ -866,11 +872,11 @@ static int cma_rep_recv(struct rdma_id_private *id_priv)
{
int ret;
- ret = cma_modify_qp_rtr(id_priv);
+ ret = cma_modify_qp_rtr(id_priv, NULL);
if (ret)
goto reject;
- ret = cma_modify_qp_rts(id_priv);
+ ret = cma_modify_qp_rts(id_priv, NULL);
if (ret)
goto reject;
@@ -1122,8 +1128,10 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
cm_id->cm_handler = cma_ib_handler;
ret = conn_id->id.event_handler(&conn_id->id, &event);
- if (!ret)
+ if (!ret) {
+ cma_enable_remove(conn_id);
goto out;
+ }
/* Destroy the CM ID by returning a non-zero value. */
conn_id->cm_id.ib = NULL;
@@ -1262,6 +1270,7 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
struct net_device *dev = NULL;
struct rdma_cm_event event;
int ret;
+ struct ib_device_attr attr;
listen_id = cm_id->context;
if (cma_disable_remove(listen_id, CMA_LISTEN))
@@ -1311,10 +1320,19 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
sin = (struct sockaddr_in *) &new_cm_id->route.addr.dst_addr;
*sin = iw_event->remote_addr;
+ ret = ib_query_device(conn_id->id.device, &attr);
+ if (ret) {
+ cma_enable_remove(conn_id);
+ rdma_destroy_id(new_cm_id);
+ goto out;
+ }
+
memset(&event, 0, sizeof event);
event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
event.param.conn.private_data = iw_event->private_data;
event.param.conn.private_data_len = iw_event->private_data_len;
+ event.param.conn.initiator_depth = attr.max_qp_init_rd_atom;
+ event.param.conn.responder_resources = attr.max_qp_rd_atom;
ret = conn_id->id.event_handler(&conn_id->id, &event);
if (ret) {
/* User wants to destroy the CM ID */
@@ -2272,7 +2290,7 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
sin = (struct sockaddr_in*) &id_priv->id.route.addr.dst_addr;
cm_id->remote_addr = *sin;
- ret = cma_modify_qp_rtr(id_priv);
+ ret = cma_modify_qp_rtr(id_priv, conn_param);
if (ret)
goto out;
@@ -2335,25 +2353,15 @@ static int cma_accept_ib(struct rdma_id_private *id_priv,
struct rdma_conn_param *conn_param)
{
struct ib_cm_rep_param rep;
- struct ib_qp_attr qp_attr;
- int qp_attr_mask, ret;
-
- if (id_priv->id.qp) {
- ret = cma_modify_qp_rtr(id_priv);
- if (ret)
- goto out;
+ int ret;
- qp_attr.qp_state = IB_QPS_RTS;
- ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, &qp_attr,
- &qp_attr_mask);
- if (ret)
- goto out;
+ ret = cma_modify_qp_rtr(id_priv, conn_param);
+ if (ret)
+ goto out;
- qp_attr.max_rd_atomic = conn_param->initiator_depth;
- ret = ib_modify_qp(id_priv->id.qp, &qp_attr, qp_attr_mask);
- if (ret)
- goto out;
- }
+ ret = cma_modify_qp_rts(id_priv, conn_param);
+ if (ret)
+ goto out;
memset(&rep, 0, sizeof rep);
rep.qp_num = id_priv->qp_num;
@@ -2378,7 +2386,7 @@ static int cma_accept_iw(struct rdma_id_private *id_priv,
struct iw_cm_conn_param iw_param;
int ret;
- ret = cma_modify_qp_rtr(id_priv);
+ ret = cma_modify_qp_rtr(id_priv, conn_param);
if (ret)
return ret;
@@ -2598,11 +2606,9 @@ static void cma_set_mgid(struct rdma_id_private *id_priv,
/* IPv6 address is an SA assigned MGID. */
memcpy(mgid, &sin6->sin6_addr, sizeof *mgid);
} else {
- ip_ib_mc_map(sin->sin_addr.s_addr, mc_map);
+ ip_ib_mc_map(sin->sin_addr.s_addr, dev_addr->broadcast, mc_map);
if (id_priv->id.ps == RDMA_PS_UDP)
mc_map[7] = 0x01; /* Use RDMA CM signature */
- mc_map[8] = ib_addr_get_pkey(dev_addr) >> 8;
- mc_map[9] = (unsigned char) ib_addr_get_pkey(dev_addr);
*mgid = *(union ib_gid *) (mc_map + 4);
}
}
diff --git a/trunk/drivers/infiniband/core/fmr_pool.c b/trunk/drivers/infiniband/core/fmr_pool.c
index e8d5f6b64998..6c7aa59794d4 100644
--- a/trunk/drivers/infiniband/core/fmr_pool.c
+++ b/trunk/drivers/infiniband/core/fmr_pool.c
@@ -139,7 +139,7 @@ static inline struct ib_pool_fmr *ib_fmr_cache_lookup(struct ib_fmr_pool *pool,
static void ib_fmr_batch_release(struct ib_fmr_pool *pool)
{
int ret;
- struct ib_pool_fmr *fmr;
+ struct ib_pool_fmr *fmr, *next;
LIST_HEAD(unmap_list);
LIST_HEAD(fmr_list);
@@ -158,6 +158,20 @@ static void ib_fmr_batch_release(struct ib_fmr_pool *pool)
#endif
}
+ /*
+ * The free_list may hold FMRs that have been put there
+ * because they haven't reached the max_remap count.
+ * Invalidate their mapping as well.
+ */
+ list_for_each_entry_safe(fmr, next, &pool->free_list, list) {
+ if (fmr->remap_count == 0)
+ continue;
+ hlist_del_init(&fmr->cache_node);
+ fmr->remap_count = 0;
+ list_add_tail(&fmr->fmr->list, &fmr_list);
+ list_move(&fmr->list, &unmap_list);
+ }
+
list_splice(&pool->dirty_list, &unmap_list);
INIT_LIST_HEAD(&pool->dirty_list);
pool->dirty_len = 0;
@@ -182,8 +196,7 @@ static int ib_fmr_cleanup_thread(void *pool_ptr)
struct ib_fmr_pool *pool = pool_ptr;
do {
- if (pool->dirty_len >= pool->dirty_watermark ||
- atomic_read(&pool->flush_ser) - atomic_read(&pool->req_ser) < 0) {
+ if (atomic_read(&pool->flush_ser) - atomic_read(&pool->req_ser) < 0) {
ib_fmr_batch_release(pool);
atomic_inc(&pool->flush_ser);
@@ -194,8 +207,7 @@ static int ib_fmr_cleanup_thread(void *pool_ptr)
}
set_current_state(TASK_INTERRUPTIBLE);
- if (pool->dirty_len < pool->dirty_watermark &&
- atomic_read(&pool->flush_ser) - atomic_read(&pool->req_ser) >= 0 &&
+ if (atomic_read(&pool->flush_ser) - atomic_read(&pool->req_ser) >= 0 &&
!kthread_should_stop())
schedule();
__set_current_state(TASK_RUNNING);
@@ -369,11 +381,6 @@ void ib_destroy_fmr_pool(struct ib_fmr_pool *pool)
i = 0;
list_for_each_entry_safe(fmr, tmp, &pool->free_list, list) {
- if (fmr->remap_count) {
- INIT_LIST_HEAD(&fmr_list);
- list_add_tail(&fmr->fmr->list, &fmr_list);
- ib_unmap_fmr(&fmr_list);
- }
ib_dealloc_fmr(fmr->fmr);
list_del(&fmr->list);
kfree(fmr);
@@ -511,8 +518,10 @@ int ib_fmr_pool_unmap(struct ib_pool_fmr *fmr)
list_add_tail(&fmr->list, &pool->free_list);
} else {
list_add_tail(&fmr->list, &pool->dirty_list);
- ++pool->dirty_len;
- wake_up_process(pool->thread);
+ if (++pool->dirty_len >= pool->dirty_watermark) {
+ atomic_inc(&pool->req_ser);
+ wake_up_process(pool->thread);
+ }
}
}
diff --git a/trunk/drivers/infiniband/core/mad.c b/trunk/drivers/infiniband/core/mad.c
index 6f4287716ab1..fbe16d5250a4 100644
--- a/trunk/drivers/infiniband/core/mad.c
+++ b/trunk/drivers/infiniband/core/mad.c
@@ -701,7 +701,8 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
}
/* Check to post send on QP or process locally */
- if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD)
+ if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD &&
+ smi_check_local_returning_smp(smp, device) == IB_SMI_DISCARD)
goto out;
local = kmalloc(sizeof *local, GFP_ATOMIC);
@@ -752,8 +753,7 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
port_priv = ib_get_mad_port(mad_agent_priv->agent.device,
mad_agent_priv->agent.port_num);
if (port_priv) {
- mad_priv->mad.mad.mad_hdr.tid =
- ((struct ib_mad *)smp)->mad_hdr.tid;
+ memcpy(&mad_priv->mad.mad, smp, sizeof(struct ib_mad));
recv_mad_agent = find_mad_agent(port_priv,
&mad_priv->mad.mad);
}
@@ -1100,7 +1100,9 @@ int ib_post_send_mad(struct ib_mad_send_buf *send_buf,
mad_send_wr->tid = ((struct ib_mad_hdr *) send_buf->mad)->tid;
/* Timeout will be updated after send completes */
mad_send_wr->timeout = msecs_to_jiffies(send_buf->timeout_ms);
- mad_send_wr->retries = send_buf->retries;
+ mad_send_wr->max_retries = send_buf->retries;
+ mad_send_wr->retries_left = send_buf->retries;
+ send_buf->retries = 0;
/* Reference for work request to QP + response */
mad_send_wr->refcount = 1 + (mad_send_wr->timeout > 0);
mad_send_wr->status = IB_WC_SUCCESS;
@@ -1931,15 +1933,6 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
if (port_priv->device->process_mad) {
int ret;
- if (!response) {
- printk(KERN_ERR PFX "No memory for response MAD\n");
- /*
- * Is it better to assume that
- * it wouldn't be processed ?
- */
- goto out;
- }
-
ret = port_priv->device->process_mad(port_priv->device, 0,
port_priv->port_num,
wc, &recv->grh,
@@ -2282,8 +2275,6 @@ static void cancel_mads(struct ib_mad_agent_private *mad_agent_priv)
/* Empty wait list to prevent receives from finding a request */
list_splice_init(&mad_agent_priv->wait_list, &cancel_list);
- /* Empty local completion list as well */
- list_splice_init(&mad_agent_priv->local_list, &cancel_list);
spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
/* Report all cancelled requests */
@@ -2445,9 +2436,12 @@ static int retry_send(struct ib_mad_send_wr_private *mad_send_wr)
{
int ret;
- if (!mad_send_wr->retries--)
+ if (!mad_send_wr->retries_left)
return -ETIMEDOUT;
+ mad_send_wr->retries_left--;
+ mad_send_wr->send_buf.retries++;
+
mad_send_wr->timeout = msecs_to_jiffies(mad_send_wr->send_buf.timeout_ms);
if (mad_send_wr->mad_agent_priv->agent.rmpp_version) {
diff --git a/trunk/drivers/infiniband/core/mad_priv.h b/trunk/drivers/infiniband/core/mad_priv.h
index 9be5cc00a3a9..8b75010016ec 100644
--- a/trunk/drivers/infiniband/core/mad_priv.h
+++ b/trunk/drivers/infiniband/core/mad_priv.h
@@ -131,7 +131,8 @@ struct ib_mad_send_wr_private {
struct ib_sge sg_list[IB_MAD_SEND_REQ_MAX_SG];
__be64 tid;
unsigned long timeout;
- int retries;
+ int max_retries;
+ int retries_left;
int retry;
int refcount;
enum ib_wc_status status;
diff --git a/trunk/drivers/infiniband/core/mad_rmpp.c b/trunk/drivers/infiniband/core/mad_rmpp.c
index d43bc62005b3..a5e2a310f312 100644
--- a/trunk/drivers/infiniband/core/mad_rmpp.c
+++ b/trunk/drivers/infiniband/core/mad_rmpp.c
@@ -684,7 +684,7 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
if (seg_num > mad_send_wr->last_ack) {
adjust_last_ack(mad_send_wr, seg_num);
- mad_send_wr->retries = mad_send_wr->send_buf.retries;
+ mad_send_wr->retries_left = mad_send_wr->max_retries;
}
mad_send_wr->newwin = newwin;
if (mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) {
diff --git a/trunk/drivers/infiniband/core/multicast.c b/trunk/drivers/infiniband/core/multicast.c
index 1bc1fe605282..107f170c57cd 100644
--- a/trunk/drivers/infiniband/core/multicast.c
+++ b/trunk/drivers/infiniband/core/multicast.c
@@ -73,11 +73,20 @@ struct mcast_device {
};
enum mcast_state {
- MCAST_IDLE,
MCAST_JOINING,
MCAST_MEMBER,
+ MCAST_ERROR,
+};
+
+enum mcast_group_state {
+ MCAST_IDLE,
MCAST_BUSY,
- MCAST_ERROR
+ MCAST_GROUP_ERROR,
+ MCAST_PKEY_EVENT
+};
+
+enum {
+ MCAST_INVALID_PKEY_INDEX = 0xFFFF
};
struct mcast_member;
@@ -93,9 +102,10 @@ struct mcast_group {
struct mcast_member *last_join;
int members[3];
atomic_t refcount;
- enum mcast_state state;
+ enum mcast_group_state state;
struct ib_sa_query *query;
int query_id;
+ u16 pkey_index;
};
struct mcast_member {
@@ -378,9 +388,19 @@ static int fail_join(struct mcast_group *group, struct mcast_member *member,
static void process_group_error(struct mcast_group *group)
{
struct mcast_member *member;
- int ret;
+ int ret = 0;
+ u16 pkey_index;
+
+ if (group->state == MCAST_PKEY_EVENT)
+ ret = ib_find_pkey(group->port->dev->device,
+ group->port->port_num,
+ be16_to_cpu(group->rec.pkey), &pkey_index);
spin_lock_irq(&group->lock);
+ if (group->state == MCAST_PKEY_EVENT && !ret &&
+ group->pkey_index == pkey_index)
+ goto out;
+
while (!list_empty(&group->active_list)) {
member = list_entry(group->active_list.next,
struct mcast_member, list);
@@ -399,6 +419,7 @@ static void process_group_error(struct mcast_group *group)
}
group->rec.join_state = 0;
+out:
group->state = MCAST_BUSY;
spin_unlock_irq(&group->lock);
}
@@ -415,9 +436,9 @@ static void mcast_work_handler(struct work_struct *work)
retest:
spin_lock_irq(&group->lock);
while (!list_empty(&group->pending_list) ||
- (group->state == MCAST_ERROR)) {
+ (group->state != MCAST_BUSY)) {
- if (group->state == MCAST_ERROR) {
+ if (group->state != MCAST_BUSY) {
spin_unlock_irq(&group->lock);
process_group_error(group);
goto retest;
@@ -494,12 +515,19 @@ static void join_handler(int status, struct ib_sa_mcmember_rec *rec,
void *context)
{
struct mcast_group *group = context;
+ u16 pkey_index = MCAST_INVALID_PKEY_INDEX;
if (status)
process_join_error(group, status);
else {
+ ib_find_pkey(group->port->dev->device, group->port->port_num,
+ be16_to_cpu(rec->pkey), &pkey_index);
+
spin_lock_irq(&group->port->lock);
group->rec = *rec;
+ if (group->state == MCAST_BUSY &&
+ group->pkey_index == MCAST_INVALID_PKEY_INDEX)
+ group->pkey_index = pkey_index;
if (!memcmp(&mgid0, &group->rec.mgid, sizeof mgid0)) {
rb_erase(&group->node, &group->port->table);
mcast_insert(group->port, group, 1);
@@ -539,6 +567,7 @@ static struct mcast_group *acquire_group(struct mcast_port *port,
group->port = port;
group->rec.mgid = *mgid;
+ group->pkey_index = MCAST_INVALID_PKEY_INDEX;
INIT_LIST_HEAD(&group->pending_list);
INIT_LIST_HEAD(&group->active_list);
INIT_WORK(&group->work, mcast_work_handler);
@@ -707,7 +736,8 @@ int ib_init_ah_from_mcmember(struct ib_device *device, u8 port_num,
}
EXPORT_SYMBOL(ib_init_ah_from_mcmember);
-static void mcast_groups_lost(struct mcast_port *port)
+static void mcast_groups_event(struct mcast_port *port,
+ enum mcast_group_state state)
{
struct mcast_group *group;
struct rb_node *node;
@@ -721,7 +751,8 @@ static void mcast_groups_lost(struct mcast_port *port)
atomic_inc(&group->refcount);
queue_work(mcast_wq, &group->work);
}
- group->state = MCAST_ERROR;
+ if (group->state != MCAST_GROUP_ERROR)
+ group->state = state;
spin_unlock(&group->lock);
}
spin_unlock_irqrestore(&port->lock, flags);
@@ -731,16 +762,20 @@ static void mcast_event_handler(struct ib_event_handler *handler,
struct ib_event *event)
{
struct mcast_device *dev;
+ int index;
dev = container_of(handler, struct mcast_device, event_handler);
+ index = event->element.port_num - dev->start_port;
switch (event->event) {
case IB_EVENT_PORT_ERR:
case IB_EVENT_LID_CHANGE:
case IB_EVENT_SM_CHANGE:
case IB_EVENT_CLIENT_REREGISTER:
- mcast_groups_lost(&dev->port[event->element.port_num -
- dev->start_port]);
+ mcast_groups_event(&dev->port[index], MCAST_GROUP_ERROR);
+ break;
+ case IB_EVENT_PKEY_CHANGE:
+ mcast_groups_event(&dev->port[index], MCAST_PKEY_EVENT);
break;
default:
break;
diff --git a/trunk/drivers/infiniband/core/smi.h b/trunk/drivers/infiniband/core/smi.h
index 1cfc2984434f..aff96bac49b4 100644
--- a/trunk/drivers/infiniband/core/smi.h
+++ b/trunk/drivers/infiniband/core/smi.h
@@ -59,7 +59,8 @@ extern enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
u8 node_type, int port_num);
/*
- * Return 1 if the SMP should be handled by the local SMA/SM via process_mad
+ * Return IB_SMI_HANDLE if the SMP should be handled by the local SMA/SM
+ * via process_mad
*/
static inline enum smi_action smi_check_local_smp(struct ib_smp *smp,
struct ib_device *device)
@@ -71,4 +72,19 @@ static inline enum smi_action smi_check_local_smp(struct ib_smp *smp,
(smp->hop_ptr == smp->hop_cnt + 1)) ?
IB_SMI_HANDLE : IB_SMI_DISCARD);
}
+
+/*
+ * Return IB_SMI_HANDLE if the SMP should be handled by the local SMA/SM
+ * via process_mad
+ */
+static inline enum smi_action smi_check_local_returning_smp(struct ib_smp *smp,
+ struct ib_device *device)
+{
+ /* C14-13:3 -- We're at the end of the DR segment of path */
+ /* C14-13:4 -- Hop Pointer == 0 -> give to SM */
+ return ((device->process_mad &&
+ ib_get_smp_direction(smp) &&
+ !smp->hop_ptr) ? IB_SMI_HANDLE : IB_SMI_DISCARD);
+}
+
#endif /* __SMI_H_ */
diff --git a/trunk/drivers/infiniband/core/ucm.c b/trunk/drivers/infiniband/core/ucm.c
index 424983f5b1ee..4291ab42a5b9 100644
--- a/trunk/drivers/infiniband/core/ucm.c
+++ b/trunk/drivers/infiniband/core/ucm.c
@@ -106,6 +106,9 @@ enum {
IB_UCM_MAX_DEVICES = 32
};
+/* ib_cm and ib_user_cm modules share /sys/class/infiniband_cm */
+extern struct class cm_class;
+
#define IB_UCM_BASE_DEV MKDEV(IB_UCM_MAJOR, IB_UCM_BASE_MINOR)
static void ib_ucm_add_one(struct ib_device *device);
@@ -1199,7 +1202,7 @@ static int ib_ucm_close(struct inode *inode, struct file *filp)
return 0;
}
-static void ib_ucm_release_class_dev(struct class_device *class_dev)
+static void ucm_release_class_dev(struct class_device *class_dev)
{
struct ib_ucm_device *dev;
@@ -1217,11 +1220,6 @@ static const struct file_operations ucm_fops = {
.poll = ib_ucm_poll,
};
-static struct class ucm_class = {
- .name = "infiniband_cm",
- .release = ib_ucm_release_class_dev
-};
-
static ssize_t show_ibdev(struct class_device *class_dev, char *buf)
{
struct ib_ucm_device *dev;
@@ -1257,9 +1255,10 @@ static void ib_ucm_add_one(struct ib_device *device)
if (cdev_add(&ucm_dev->dev, IB_UCM_BASE_DEV + ucm_dev->devnum, 1))
goto err;
- ucm_dev->class_dev.class = &ucm_class;
+ ucm_dev->class_dev.class = &cm_class;
ucm_dev->class_dev.dev = device->dma_device;
ucm_dev->class_dev.devt = ucm_dev->dev.dev;
+ ucm_dev->class_dev.release = ucm_release_class_dev;
snprintf(ucm_dev->class_dev.class_id, BUS_ID_SIZE, "ucm%d",
ucm_dev->devnum);
if (class_device_register(&ucm_dev->class_dev))
@@ -1306,40 +1305,34 @@ static int __init ib_ucm_init(void)
"infiniband_cm");
if (ret) {
printk(KERN_ERR "ucm: couldn't register device number\n");
- goto err;
+ goto error1;
}
- ret = class_register(&ucm_class);
- if (ret) {
- printk(KERN_ERR "ucm: couldn't create class infiniband_cm\n");
- goto err_chrdev;
- }
-
- ret = class_create_file(&ucm_class, &class_attr_abi_version);
+ ret = class_create_file(&cm_class, &class_attr_abi_version);
if (ret) {
printk(KERN_ERR "ucm: couldn't create abi_version attribute\n");
- goto err_class;
+ goto error2;
}
ret = ib_register_client(&ucm_client);
if (ret) {
printk(KERN_ERR "ucm: couldn't register client\n");
- goto err_class;
+ goto error3;
}
return 0;
-err_class:
- class_unregister(&ucm_class);
-err_chrdev:
+error3:
+ class_remove_file(&cm_class, &class_attr_abi_version);
+error2:
unregister_chrdev_region(IB_UCM_BASE_DEV, IB_UCM_MAX_DEVICES);
-err:
+error1:
return ret;
}
static void __exit ib_ucm_cleanup(void)
{
ib_unregister_client(&ucm_client);
- class_unregister(&ucm_class);
+ class_remove_file(&cm_class, &class_attr_abi_version);
unregister_chrdev_region(IB_UCM_BASE_DEV, IB_UCM_MAX_DEVICES);
idr_destroy(&ctx_id_table);
}
diff --git a/trunk/drivers/infiniband/core/ucma.c b/trunk/drivers/infiniband/core/ucma.c
index 90d675ad9ec8..15937eb38aae 100644
--- a/trunk/drivers/infiniband/core/ucma.c
+++ b/trunk/drivers/infiniband/core/ucma.c
@@ -31,6 +31,7 @@
*/
#include
+#include
#include
#include
#include
@@ -991,6 +992,96 @@ static ssize_t ucma_leave_multicast(struct ucma_file *file,
return ret;
}
+static void ucma_lock_files(struct ucma_file *file1, struct ucma_file *file2)
+{
+ /* Acquire mutex's based on pointer comparison to prevent deadlock. */
+ if (file1 < file2) {
+ mutex_lock(&file1->mut);
+ mutex_lock(&file2->mut);
+ } else {
+ mutex_lock(&file2->mut);
+ mutex_lock(&file1->mut);
+ }
+}
+
+static void ucma_unlock_files(struct ucma_file *file1, struct ucma_file *file2)
+{
+ if (file1 < file2) {
+ mutex_unlock(&file2->mut);
+ mutex_unlock(&file1->mut);
+ } else {
+ mutex_unlock(&file1->mut);
+ mutex_unlock(&file2->mut);
+ }
+}
+
+static void ucma_move_events(struct ucma_context *ctx, struct ucma_file *file)
+{
+ struct ucma_event *uevent, *tmp;
+
+ list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list)
+ if (uevent->ctx == ctx)
+ list_move_tail(&uevent->list, &file->event_list);
+}
+
+static ssize_t ucma_migrate_id(struct ucma_file *new_file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_migrate_id cmd;
+ struct rdma_ucm_migrate_resp resp;
+ struct ucma_context *ctx;
+ struct file *filp;
+ struct ucma_file *cur_file;
+ int ret = 0;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ /* Get current fd to protect against it being closed */
+ filp = fget(cmd.fd);
+ if (!filp)
+ return -ENOENT;
+
+ /* Validate current fd and prevent destruction of id. */
+ ctx = ucma_get_ctx(filp->private_data, cmd.id);
+ if (IS_ERR(ctx)) {
+ ret = PTR_ERR(ctx);
+ goto file_put;
+ }
+
+ cur_file = ctx->file;
+ if (cur_file == new_file) {
+ resp.events_reported = ctx->events_reported;
+ goto response;
+ }
+
+ /*
+ * Migrate events between fd's, maintaining order, and avoiding new
+ * events being added before existing events.
+ */
+ ucma_lock_files(cur_file, new_file);
+ mutex_lock(&mut);
+
+ list_move_tail(&ctx->list, &new_file->ctx_list);
+ ucma_move_events(ctx, new_file);
+ ctx->file = new_file;
+ resp.events_reported = ctx->events_reported;
+
+ mutex_unlock(&mut);
+ ucma_unlock_files(cur_file, new_file);
+
+response:
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+ ucma_put_ctx(ctx);
+file_put:
+ fput(filp);
+ return ret;
+}
+
static ssize_t (*ucma_cmd_table[])(struct ucma_file *file,
const char __user *inbuf,
int in_len, int out_len) = {
@@ -1012,6 +1103,7 @@ static ssize_t (*ucma_cmd_table[])(struct ucma_file *file,
[RDMA_USER_CM_CMD_NOTIFY] = ucma_notify,
[RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast,
[RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast,
+ [RDMA_USER_CM_CMD_MIGRATE_ID] = ucma_migrate_id
};
static ssize_t ucma_write(struct file *filp, const char __user *buf,
diff --git a/trunk/drivers/infiniband/core/user_mad.c b/trunk/drivers/infiniband/core/user_mad.c
index b53eac4611de..4e915104ac4c 100644
--- a/trunk/drivers/infiniband/core/user_mad.c
+++ b/trunk/drivers/infiniband/core/user_mad.c
@@ -2,6 +2,7 @@
* Copyright (c) 2004 Topspin Communications. All rights reserved.
* Copyright (c) 2005 Voltaire, Inc. All rights reserved.
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2008 Cisco. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -42,7 +43,7 @@
#include
#include
#include
-#include
+#include
#include
#include
@@ -94,7 +95,7 @@ struct ib_umad_port {
struct class_device *sm_class_dev;
struct semaphore sm_sem;
- struct rw_semaphore mutex;
+ struct mutex file_mutex;
struct list_head file_list;
struct ib_device *ib_dev;
@@ -110,11 +111,11 @@ struct ib_umad_device {
};
struct ib_umad_file {
+ struct mutex mutex;
struct ib_umad_port *port;
struct list_head recv_list;
struct list_head send_list;
struct list_head port_list;
- spinlock_t recv_lock;
spinlock_t send_lock;
wait_queue_head_t recv_wait;
struct ib_mad_agent *agent[IB_UMAD_MAX_AGENTS];
@@ -156,7 +157,7 @@ static int hdr_size(struct ib_umad_file *file)
sizeof (struct ib_user_mad_hdr_old);
}
-/* caller must hold port->mutex at least for reading */
+/* caller must hold file->mutex */
static struct ib_mad_agent *__get_agent(struct ib_umad_file *file, int id)
{
return file->agents_dead ? NULL : file->agent[id];
@@ -168,32 +169,30 @@ static int queue_packet(struct ib_umad_file *file,
{
int ret = 1;
- down_read(&file->port->mutex);
+ mutex_lock(&file->mutex);
for (packet->mad.hdr.id = 0;
packet->mad.hdr.id < IB_UMAD_MAX_AGENTS;
packet->mad.hdr.id++)
if (agent == __get_agent(file, packet->mad.hdr.id)) {
- spin_lock_irq(&file->recv_lock);
list_add_tail(&packet->list, &file->recv_list);
- spin_unlock_irq(&file->recv_lock);
wake_up_interruptible(&file->recv_wait);
ret = 0;
break;
}
- up_read(&file->port->mutex);
+ mutex_unlock(&file->mutex);
return ret;
}
static void dequeue_send(struct ib_umad_file *file,
struct ib_umad_packet *packet)
- {
+{
spin_lock_irq(&file->send_lock);
list_del(&packet->list);
spin_unlock_irq(&file->send_lock);
- }
+}
static void send_handler(struct ib_mad_agent *agent,
struct ib_mad_send_wc *send_wc)
@@ -341,10 +340,10 @@ static ssize_t ib_umad_read(struct file *filp, char __user *buf,
if (count < hdr_size(file))
return -EINVAL;
- spin_lock_irq(&file->recv_lock);
+ mutex_lock(&file->mutex);
while (list_empty(&file->recv_list)) {
- spin_unlock_irq(&file->recv_lock);
+ mutex_unlock(&file->mutex);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
@@ -353,13 +352,13 @@ static ssize_t ib_umad_read(struct file *filp, char __user *buf,
!list_empty(&file->recv_list)))
return -ERESTARTSYS;
- spin_lock_irq(&file->recv_lock);
+ mutex_lock(&file->mutex);
}
packet = list_entry(file->recv_list.next, struct ib_umad_packet, list);
list_del(&packet->list);
- spin_unlock_irq(&file->recv_lock);
+ mutex_unlock(&file->mutex);
if (packet->recv_wc)
ret = copy_recv_mad(file, buf, packet, count);
@@ -368,9 +367,9 @@ static ssize_t ib_umad_read(struct file *filp, char __user *buf,
if (ret < 0) {
/* Requeue packet */
- spin_lock_irq(&file->recv_lock);
+ mutex_lock(&file->mutex);
list_add(&packet->list, &file->recv_list);
- spin_unlock_irq(&file->recv_lock);
+ mutex_unlock(&file->mutex);
} else {
if (packet->recv_wc)
ib_free_recv_mad(packet->recv_wc);
@@ -481,7 +480,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
goto err;
}
- down_read(&file->port->mutex);
+ mutex_lock(&file->mutex);
agent = __get_agent(file, packet->mad.hdr.id);
if (!agent) {
@@ -577,7 +576,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
if (ret)
goto err_send;
- up_read(&file->port->mutex);
+ mutex_unlock(&file->mutex);
return count;
err_send:
@@ -587,7 +586,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
err_ah:
ib_destroy_ah(ah);
err_up:
- up_read(&file->port->mutex);
+ mutex_unlock(&file->mutex);
err:
kfree(packet);
return ret;
@@ -613,11 +612,12 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, void __user *arg,
{
struct ib_user_mad_reg_req ureq;
struct ib_mad_reg_req req;
- struct ib_mad_agent *agent;
+ struct ib_mad_agent *agent = NULL;
int agent_id;
int ret;
- down_write(&file->port->mutex);
+ mutex_lock(&file->port->file_mutex);
+ mutex_lock(&file->mutex);
if (!file->port->ib_dev) {
ret = -EPIPE;
@@ -666,13 +666,13 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, void __user *arg,
send_handler, recv_handler, file);
if (IS_ERR(agent)) {
ret = PTR_ERR(agent);
+ agent = NULL;
goto out;
}
if (put_user(agent_id,
(u32 __user *) (arg + offsetof(struct ib_user_mad_reg_req, id)))) {
ret = -EFAULT;
- ib_unregister_mad_agent(agent);
goto out;
}
@@ -690,7 +690,13 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, void __user *arg,
ret = 0;
out:
- up_write(&file->port->mutex);
+ mutex_unlock(&file->mutex);
+
+ if (ret && agent)
+ ib_unregister_mad_agent(agent);
+
+ mutex_unlock(&file->port->file_mutex);
+
return ret;
}
@@ -703,7 +709,8 @@ static int ib_umad_unreg_agent(struct ib_umad_file *file, u32 __user *arg)
if (get_user(id, arg))
return -EFAULT;
- down_write(&file->port->mutex);
+ mutex_lock(&file->port->file_mutex);
+ mutex_lock(&file->mutex);
if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !__get_agent(file, id)) {
ret = -EINVAL;
@@ -714,11 +721,13 @@ static int ib_umad_unreg_agent(struct ib_umad_file *file, u32 __user *arg)
file->agent[id] = NULL;
out:
- up_write(&file->port->mutex);
+ mutex_unlock(&file->mutex);
if (agent)
ib_unregister_mad_agent(agent);
+ mutex_unlock(&file->port->file_mutex);
+
return ret;
}
@@ -726,12 +735,12 @@ static long ib_umad_enable_pkey(struct ib_umad_file *file)
{
int ret = 0;
- down_write(&file->port->mutex);
+ mutex_lock(&file->mutex);
if (file->already_used)
ret = -EINVAL;
else
file->use_pkey_index = 1;
- up_write(&file->port->mutex);
+ mutex_unlock(&file->mutex);
return ret;
}
@@ -783,7 +792,7 @@ static int ib_umad_open(struct inode *inode, struct file *filp)
if (!port)
return -ENXIO;
- down_write(&port->mutex);
+ mutex_lock(&port->file_mutex);
if (!port->ib_dev) {
ret = -ENXIO;
@@ -797,7 +806,7 @@ static int ib_umad_open(struct inode *inode, struct file *filp)
goto out;
}
- spin_lock_init(&file->recv_lock);
+ mutex_init(&file->mutex);
spin_lock_init(&file->send_lock);
INIT_LIST_HEAD(&file->recv_list);
INIT_LIST_HEAD(&file->send_list);
@@ -809,7 +818,7 @@ static int ib_umad_open(struct inode *inode, struct file *filp)
list_add_tail(&file->port_list, &port->file_list);
out:
- up_write(&port->mutex);
+ mutex_unlock(&port->file_mutex);
return ret;
}
@@ -821,7 +830,8 @@ static int ib_umad_close(struct inode *inode, struct file *filp)
int already_dead;
int i;
- down_write(&file->port->mutex);
+ mutex_lock(&file->port->file_mutex);
+ mutex_lock(&file->mutex);
already_dead = file->agents_dead;
file->agents_dead = 1;
@@ -834,14 +844,14 @@ static int ib_umad_close(struct inode *inode, struct file *filp)
list_del(&file->port_list);
- downgrade_write(&file->port->mutex);
+ mutex_unlock(&file->mutex);
if (!already_dead)
for (i = 0; i < IB_UMAD_MAX_AGENTS; ++i)
if (file->agent[i])
ib_unregister_mad_agent(file->agent[i]);
- up_read(&file->port->mutex);
+ mutex_unlock(&file->port->file_mutex);
kfree(file);
kref_put(&dev->ref, ib_umad_release_dev);
@@ -914,10 +924,10 @@ static int ib_umad_sm_close(struct inode *inode, struct file *filp)
};
int ret = 0;
- down_write(&port->mutex);
+ mutex_lock(&port->file_mutex);
if (port->ib_dev)
ret = ib_modify_port(port->ib_dev, port->port_num, 0, &props);
- up_write(&port->mutex);
+ mutex_unlock(&port->file_mutex);
up(&port->sm_sem);
@@ -981,7 +991,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
port->ib_dev = device;
port->port_num = port_num;
init_MUTEX(&port->sm_sem);
- init_rwsem(&port->mutex);
+ mutex_init(&port->file_mutex);
INIT_LIST_HEAD(&port->file_list);
port->dev = cdev_alloc();
@@ -1052,6 +1062,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
static void ib_umad_kill_port(struct ib_umad_port *port)
{
struct ib_umad_file *file;
+ int already_dead;
int id;
class_set_devdata(port->class_dev, NULL);
@@ -1067,42 +1078,22 @@ static void ib_umad_kill_port(struct ib_umad_port *port)
umad_port[port->dev_num] = NULL;
spin_unlock(&port_lock);
- down_write(&port->mutex);
+ mutex_lock(&port->file_mutex);
port->ib_dev = NULL;
- /*
- * Now go through the list of files attached to this port and
- * unregister all of their MAD agents. We need to hold
- * port->mutex while doing this to avoid racing with
- * ib_umad_close(), but we can't hold the mutex for writing
- * while calling ib_unregister_mad_agent(), since that might
- * deadlock by calling back into queue_packet(). So we
- * downgrade our lock to a read lock, and then drop and
- * reacquire the write lock for the next iteration.
- *
- * We do list_del_init() on the file's list_head so that the
- * list_del in ib_umad_close() is still OK, even after the
- * file is removed from the list.
- */
- while (!list_empty(&port->file_list)) {
- file = list_entry(port->file_list.next, struct ib_umad_file,
- port_list);
-
+ list_for_each_entry(file, &port->file_list, port_list) {
+ mutex_lock(&file->mutex);
+ already_dead = file->agents_dead;
file->agents_dead = 1;
- list_del_init(&file->port_list);
-
- downgrade_write(&port->mutex);
+ mutex_unlock(&file->mutex);
for (id = 0; id < IB_UMAD_MAX_AGENTS; ++id)
if (file->agent[id])
ib_unregister_mad_agent(file->agent[id]);
-
- up_read(&port->mutex);
- down_write(&port->mutex);
}
- up_write(&port->mutex);
+ mutex_unlock(&port->file_mutex);
clear_bit(port->dev_num, dev_map);
}
diff --git a/trunk/drivers/infiniband/hw/cxgb3/cxio_hal.c b/trunk/drivers/infiniband/hw/cxgb3/cxio_hal.c
index eec6a30840ca..03c5ff62889a 100644
--- a/trunk/drivers/infiniband/hw/cxgb3/cxio_hal.c
+++ b/trunk/drivers/infiniband/hw/cxgb3/cxio_hal.c
@@ -179,7 +179,7 @@ int cxio_create_cq(struct cxio_rdev *rdev_p, struct t3_cq *cq)
setup.size = 1UL << cq->size_log2;
setup.credits = 65535;
setup.credit_thres = 1;
- if (rdev_p->t3cdev_p->type == T3B)
+ if (rdev_p->t3cdev_p->type != T3A)
setup.ovfl_mode = 0;
else
setup.ovfl_mode = 1;
@@ -584,7 +584,7 @@ static int cxio_hal_ctrl_qp_write_mem(struct cxio_rdev *rdev_p, u32 addr,
{
u32 i, nr_wqe, copy_len;
u8 *copy_data;
- u8 wr_len, utx_len; /* lenght in 8 byte flit */
+ u8 wr_len, utx_len; /* length in 8 byte flit */
enum t3_wr_flags flag;
__be64 *wqe;
u64 utx_cmd;
diff --git a/trunk/drivers/infiniband/hw/cxgb3/cxio_wr.h b/trunk/drivers/infiniband/hw/cxgb3/cxio_wr.h
index c84d4ac49355..969d4d928455 100644
--- a/trunk/drivers/infiniband/hw/cxgb3/cxio_wr.h
+++ b/trunk/drivers/infiniband/hw/cxgb3/cxio_wr.h
@@ -315,7 +315,7 @@ struct t3_rdma_init_wr {
__be32 ird;
__be64 qp_dma_addr; /* 7 */
__be32 qp_dma_size; /* 8 */
- u32 irs;
+ __be32 irs;
};
struct t3_genbit {
@@ -324,7 +324,8 @@ struct t3_genbit {
};
enum rdma_init_wr_flags {
- RECVS_POSTED = 1,
+ RECVS_POSTED = (1<<0),
+ PRIV_QP = (1<<1),
};
union t3_wr {
diff --git a/trunk/drivers/infiniband/hw/cxgb3/iwch_cm.c b/trunk/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 20ba372dd182..f8cb0fe748c3 100644
--- a/trunk/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/trunk/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -1118,7 +1118,7 @@ static int act_open_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
status2errno(rpl->status));
connect_reply_upcall(ep, status2errno(rpl->status));
state_set(&ep->com, DEAD);
- if (ep->com.tdev->type == T3B && act_open_has_tid(rpl->status))
+ if (ep->com.tdev->type != T3A && act_open_has_tid(rpl->status))
release_tid(ep->com.tdev, GET_TID(rpl), NULL);
cxgb3_free_atid(ep->com.tdev, ep->atid);
dst_release(ep->dst);
@@ -1249,7 +1249,7 @@ static void reject_cr(struct t3cdev *tdev, u32 hwtid, __be32 peer_ip,
skb_trim(skb, sizeof(struct cpl_tid_release));
skb_get(skb);
- if (tdev->type == T3B)
+ if (tdev->type != T3A)
release_tid(tdev, hwtid, skb);
else {
struct cpl_pass_accept_rpl *rpl;
diff --git a/trunk/drivers/infiniband/hw/cxgb3/iwch_mem.c b/trunk/drivers/infiniband/hw/cxgb3/iwch_mem.c
index a6c2c4ba29e6..73bfd1656f86 100644
--- a/trunk/drivers/infiniband/hw/cxgb3/iwch_mem.c
+++ b/trunk/drivers/infiniband/hw/cxgb3/iwch_mem.c
@@ -122,6 +122,13 @@ int build_phys_page_list(struct ib_phys_buf *buffer_list,
*total_size += buffer_list[i].size;
if (i > 0)
mask |= buffer_list[i].addr;
+ else
+ mask |= buffer_list[i].addr & PAGE_MASK;
+ if (i != num_phys_buf - 1)
+ mask |= buffer_list[i].addr + buffer_list[i].size;
+ else
+ mask |= (buffer_list[i].addr + buffer_list[i].size +
+ PAGE_SIZE - 1) & PAGE_MASK;
}
if (*total_size > 0xFFFFFFFFULL)
diff --git a/trunk/drivers/infiniband/hw/cxgb3/iwch_provider.c b/trunk/drivers/infiniband/hw/cxgb3/iwch_provider.c
index b5436ca92e68..df1838f8f94d 100644
--- a/trunk/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/trunk/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -39,6 +39,7 @@
#include
#include
#include
+#include
#include
#include
@@ -645,7 +646,7 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (err)
goto err;
- if (udata && t3b_device(rhp)) {
+ if (udata && !t3a_device(rhp)) {
uresp.pbl_addr = (mhp->attr.pbl_addr -
rhp->rdev.rnic_info.pbl_base) >> 3;
PDBG("%s user resp pbl_addr 0x%x\n", __FUNCTION__,
@@ -1053,7 +1054,9 @@ static ssize_t show_fw_ver(struct class_device *cdev, char *buf)
struct net_device *lldev = dev->rdev.t3cdev_p->lldev;
PDBG("%s class dev 0x%p\n", __FUNCTION__, cdev);
+ rtnl_lock();
lldev->ethtool_ops->get_drvinfo(lldev, &info);
+ rtnl_unlock();
return sprintf(buf, "%s\n", info.fw_version);
}
@@ -1065,7 +1068,9 @@ static ssize_t show_hca(struct class_device *cdev, char *buf)
struct net_device *lldev = dev->rdev.t3cdev_p->lldev;
PDBG("%s class dev 0x%p\n", __FUNCTION__, cdev);
+ rtnl_lock();
lldev->ethtool_ops->get_drvinfo(lldev, &info);
+ rtnl_unlock();
return sprintf(buf, "%s\n", info.driver);
}
diff --git a/trunk/drivers/infiniband/hw/cxgb3/iwch_qp.c b/trunk/drivers/infiniband/hw/cxgb3/iwch_qp.c
index dd89b6b91f9c..ea2cdd73dd85 100644
--- a/trunk/drivers/infiniband/hw/cxgb3/iwch_qp.c
+++ b/trunk/drivers/infiniband/hw/cxgb3/iwch_qp.c
@@ -208,36 +208,19 @@ static int iwch_sgl2pbl_map(struct iwch_dev *rhp, struct ib_sge *sg_list,
static int iwch_build_rdma_recv(struct iwch_dev *rhp, union t3_wr *wqe,
struct ib_recv_wr *wr)
{
- int i, err = 0;
- u32 pbl_addr[4];
- u8 page_size[4];
+ int i;
if (wr->num_sge > T3_MAX_SGE)
return -EINVAL;
- err = iwch_sgl2pbl_map(rhp, wr->sg_list, wr->num_sge, pbl_addr,
- page_size);
- if (err)
- return err;
- wqe->recv.pagesz[0] = page_size[0];
- wqe->recv.pagesz[1] = page_size[1];
- wqe->recv.pagesz[2] = page_size[2];
- wqe->recv.pagesz[3] = page_size[3];
wqe->recv.num_sgle = cpu_to_be32(wr->num_sge);
for (i = 0; i < wr->num_sge; i++) {
wqe->recv.sgl[i].stag = cpu_to_be32(wr->sg_list[i].lkey);
wqe->recv.sgl[i].len = cpu_to_be32(wr->sg_list[i].length);
-
- /* to in the WQE == the offset into the page */
- wqe->recv.sgl[i].to = cpu_to_be64(((u32) wr->sg_list[i].addr) %
- (1UL << (12 + page_size[i])));
-
- /* pbl_addr is the adapters address in the PBL */
- wqe->recv.pbl_addr[i] = cpu_to_be32(pbl_addr[i]);
+ wqe->recv.sgl[i].to = cpu_to_be64(wr->sg_list[i].addr);
}
for (; i < T3_MAX_SGE; i++) {
wqe->recv.sgl[i].stag = 0;
wqe->recv.sgl[i].len = 0;
wqe->recv.sgl[i].to = 0;
- wqe->recv.pbl_addr[i] = 0;
}
return 0;
}
@@ -659,6 +642,7 @@ static void __flush_qp(struct iwch_qp *qhp, unsigned long *flag)
cxio_flush_rq(&qhp->wq, &rchp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&rchp->lock, *flag);
+ (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
/* locking heirarchy: cq lock first, then qp lock. */
spin_lock_irqsave(&schp->lock, *flag);
@@ -668,6 +652,7 @@ static void __flush_qp(struct iwch_qp *qhp, unsigned long *flag)
cxio_flush_sq(&qhp->wq, &schp->cq, count);
spin_unlock(&qhp->lock);
spin_unlock_irqrestore(&schp->lock, *flag);
+ (*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context);
/* deref */
if (atomic_dec_and_test(&qhp->refcnt))
@@ -678,7 +663,7 @@ static void __flush_qp(struct iwch_qp *qhp, unsigned long *flag)
static void flush_qp(struct iwch_qp *qhp, unsigned long *flag)
{
- if (t3b_device(qhp->rhp))
+ if (qhp->ibqp.uobject)
cxio_set_wq_in_error(&qhp->wq);
else
__flush_qp(qhp, flag);
@@ -732,6 +717,7 @@ static int rdma_init(struct iwch_dev *rhp, struct iwch_qp *qhp,
init_attr.qp_dma_addr = qhp->wq.dma_addr;
init_attr.qp_dma_size = (1UL << qhp->wq.size_log2);
init_attr.flags = rqes_posted(qhp) ? RECVS_POSTED : 0;
+ init_attr.flags |= capable(CAP_NET_BIND_SERVICE) ? PRIV_QP : 0;
init_attr.irs = qhp->ep->rcv_seq;
PDBG("%s init_attr.rq_addr 0x%x init_attr.rq_size = %d "
"flags 0x%x qpcaps 0x%x\n", __FUNCTION__,
@@ -847,10 +833,11 @@ int iwch_modify_qp(struct iwch_dev *rhp, struct iwch_qp *qhp,
disconnect = 1;
ep = qhp->ep;
}
+ flush_qp(qhp, &flag);
break;
case IWCH_QP_STATE_TERMINATE:
qhp->attr.state = IWCH_QP_STATE_TERMINATE;
- if (t3b_device(qhp->rhp))
+ if (qhp->ibqp.uobject)
cxio_set_wq_in_error(&qhp->wq);
if (!internal)
terminate = 1;
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_av.c b/trunk/drivers/infiniband/hw/ehca/ehca_av.c
index f7782c882ab4..194c1c30cf63 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_av.c
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_av.c
@@ -1,7 +1,7 @@
/*
* IBM eServer eHCA Infiniband device driver for Linux on POWER
*
- * adress vector functions
+ * address vector functions
*
* Authors: Hoang-Nam Nguyen
* Khadija Souissi
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_classes.h b/trunk/drivers/infiniband/hw/ehca/ehca_classes.h
index 74d2b72a11d8..f281d16040f5 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -94,7 +94,11 @@ struct ehca_sma_attr {
struct ehca_sport {
struct ib_cq *ibcq_aqp1;
- struct ib_qp *ibqp_aqp1;
+ struct ib_qp *ibqp_sqp[2];
+ /* lock to serialze modify_qp() calls for sqp in normal
+ * and irq path (when event PORT_ACTIVE is received first time)
+ */
+ spinlock_t mod_sqp_lock;
enum ib_port_state port_state;
struct ehca_sma_attr saved_attr;
};
@@ -141,6 +145,14 @@ enum ehca_ext_qp_type {
EQPT_SRQ = 3,
};
+/* struct to cache modify_qp()'s parms for GSI/SMI qp */
+struct ehca_mod_qp_parm {
+ int mask;
+ struct ib_qp_attr attr;
+};
+
+#define EHCA_MOD_QP_PARM_MAX 4
+
struct ehca_qp {
union {
struct ib_qp ib_qp;
@@ -164,10 +176,18 @@ struct ehca_qp {
struct ehca_cq *recv_cq;
unsigned int sqerr_purgeflag;
struct hlist_node list_entries;
+ /* array to cache modify_qp()'s parms for GSI/SMI qp */
+ struct ehca_mod_qp_parm *mod_qp_parm;
+ int mod_qp_parm_idx;
/* mmap counter for resources mapped into user space */
u32 mm_count_squeue;
u32 mm_count_rqueue;
u32 mm_count_galpa;
+ /* unsolicited ack circumvention */
+ int unsol_ack_circ;
+ int mtu_shift;
+ u32 message_count;
+ u32 packet_count;
};
#define IS_SRQ(qp) (qp->ext_type == EQPT_SRQ)
@@ -323,6 +343,7 @@ extern int ehca_port_act_time;
extern int ehca_use_hp_mr;
extern int ehca_scaling_code;
extern int ehca_lock_hcalls;
+extern int ehca_nr_ports;
struct ipzu_queue_resp {
u32 qe_size; /* queue entry size */
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_cq.c b/trunk/drivers/infiniband/hw/ehca/ehca_cq.c
index 79c25f51c21e..0467c158d4a9 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_cq.c
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_cq.c
@@ -246,7 +246,7 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
} else {
if (h_ret != H_PAGE_REGISTERED) {
ehca_err(device, "Registration of page failed "
- "ehca_cq=%p cq_num=%x h_ret=%li"
+ "ehca_cq=%p cq_num=%x h_ret=%li "
"counter=%i act_pages=%i",
my_cq, my_cq->cq_number,
h_ret, counter, param.act_pages);
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_irq.c b/trunk/drivers/infiniband/hw/ehca/ehca_irq.c
index 3f617b27b954..863b34fa9ff9 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -62,6 +62,7 @@
#define NEQE_PORT_NUMBER EHCA_BMASK_IBM( 8, 15)
#define NEQE_PORT_AVAILABILITY EHCA_BMASK_IBM(16, 16)
#define NEQE_DISRUPTIVE EHCA_BMASK_IBM(16, 16)
+#define NEQE_SPECIFIC_EVENT EHCA_BMASK_IBM(16, 23)
#define ERROR_DATA_LENGTH EHCA_BMASK_IBM(52, 63)
#define ERROR_DATA_TYPE EHCA_BMASK_IBM( 0, 7)
@@ -354,17 +355,34 @@ static void parse_ec(struct ehca_shca *shca, u64 eqe)
{
u8 ec = EHCA_BMASK_GET(NEQE_EVENT_CODE, eqe);
u8 port = EHCA_BMASK_GET(NEQE_PORT_NUMBER, eqe);
+ u8 spec_event;
+ struct ehca_sport *sport = &shca->sport[port - 1];
+ unsigned long flags;
switch (ec) {
case 0x30: /* port availability change */
if (EHCA_BMASK_GET(NEQE_PORT_AVAILABILITY, eqe)) {
- shca->sport[port - 1].port_state = IB_PORT_ACTIVE;
+ int suppress_event;
+ /* replay modify_qp for sqps */
+ spin_lock_irqsave(&sport->mod_sqp_lock, flags);
+ suppress_event = !sport->ibqp_sqp[IB_QPT_GSI];
+ if (sport->ibqp_sqp[IB_QPT_SMI])
+ ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_SMI]);
+ if (!suppress_event)
+ ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_GSI]);
+ spin_unlock_irqrestore(&sport->mod_sqp_lock, flags);
+
+ /* AQP1 was destroyed, ignore this event */
+ if (suppress_event)
+ break;
+
+ sport->port_state = IB_PORT_ACTIVE;
dispatch_port_event(shca, port, IB_EVENT_PORT_ACTIVE,
"is active");
ehca_query_sma_attr(shca, port,
- &shca->sport[port - 1].saved_attr);
+ &sport->saved_attr);
} else {
- shca->sport[port - 1].port_state = IB_PORT_DOWN;
+ sport->port_state = IB_PORT_DOWN;
dispatch_port_event(shca, port, IB_EVENT_PORT_ERR,
"is inactive");
}
@@ -378,11 +396,11 @@ static void parse_ec(struct ehca_shca *shca, u64 eqe)
ehca_warn(&shca->ib_device, "disruptive port "
"%d configuration change", port);
- shca->sport[port - 1].port_state = IB_PORT_DOWN;
+ sport->port_state = IB_PORT_DOWN;
dispatch_port_event(shca, port, IB_EVENT_PORT_ERR,
"is inactive");
- shca->sport[port - 1].port_state = IB_PORT_ACTIVE;
+ sport->port_state = IB_PORT_ACTIVE;
dispatch_port_event(shca, port, IB_EVENT_PORT_ACTIVE,
"is active");
} else
@@ -394,6 +412,16 @@ static void parse_ec(struct ehca_shca *shca, u64 eqe)
case 0x33: /* trace stopped */
ehca_err(&shca->ib_device, "Traced stopped.");
break;
+ case 0x34: /* util async event */
+ spec_event = EHCA_BMASK_GET(NEQE_SPECIFIC_EVENT, eqe);
+ if (spec_event == 0x80) /* client reregister required */
+ dispatch_port_event(shca, port,
+ IB_EVENT_CLIENT_REREGISTER,
+ "client reregister req.");
+ else
+ ehca_warn(&shca->ib_device, "Unknown util async "
+ "event %x on port %x", spec_event, port);
+ break;
default:
ehca_err(&shca->ib_device, "Unknown event code: %x on %s.",
ec, shca->ib_device.name);
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_iverbs.h b/trunk/drivers/infiniband/hw/ehca/ehca_iverbs.h
index 5485799cdc8d..c469bfde2708 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_iverbs.h
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_iverbs.h
@@ -200,4 +200,6 @@ void ehca_free_fw_ctrlblock(void *ptr);
#define ehca_free_fw_ctrlblock(ptr) free_page((unsigned long)(ptr))
#endif
+void ehca_recover_sqp(struct ib_qp *sqp);
+
#endif
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_main.c b/trunk/drivers/infiniband/hw/ehca/ehca_main.c
index c9e32b46387f..84c9b7b8669b 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_main.c
@@ -90,7 +90,8 @@ MODULE_PARM_DESC(hw_level,
"hardware level"
" (0: autosensing (default), 1: v. 0.20, 2: v. 0.21)");
MODULE_PARM_DESC(nr_ports,
- "number of connected ports (default: 2)");
+ "number of connected ports (-1: autodetect, 1: port one only, "
+ "2: two ports (default)");
MODULE_PARM_DESC(use_hp_mr,
"high performance MRs (0: no (default), 1: yes)");
MODULE_PARM_DESC(port_act_time,
@@ -511,7 +512,7 @@ static int ehca_create_aqp1(struct ehca_shca *shca, u32 port)
}
sport->ibcq_aqp1 = ibcq;
- if (sport->ibqp_aqp1) {
+ if (sport->ibqp_sqp[IB_QPT_GSI]) {
ehca_err(&shca->ib_device, "AQP1 QP is already created.");
ret = -EPERM;
goto create_aqp1;
@@ -537,7 +538,7 @@ static int ehca_create_aqp1(struct ehca_shca *shca, u32 port)
ret = PTR_ERR(ibqp);
goto create_aqp1;
}
- sport->ibqp_aqp1 = ibqp;
+ sport->ibqp_sqp[IB_QPT_GSI] = ibqp;
return 0;
@@ -550,7 +551,7 @@ static int ehca_destroy_aqp1(struct ehca_sport *sport)
{
int ret;
- ret = ib_destroy_qp(sport->ibqp_aqp1);
+ ret = ib_destroy_qp(sport->ibqp_sqp[IB_QPT_GSI]);
if (ret) {
ehca_gen_err("Cannot destroy AQP1 QP. ret=%i", ret);
return ret;
@@ -693,7 +694,7 @@ static int __devinit ehca_probe(struct of_device *dev,
struct ehca_shca *shca;
const u64 *handle;
struct ib_pd *ibpd;
- int ret;
+ int ret, i;
handle = of_get_property(dev->node, "ibm,hca-handle", NULL);
if (!handle) {
@@ -714,6 +715,8 @@ static int __devinit ehca_probe(struct of_device *dev,
return -ENOMEM;
}
mutex_init(&shca->modify_mutex);
+ for (i = 0; i < ARRAY_SIZE(shca->sport); i++)
+ spin_lock_init(&shca->sport[i].mod_sqp_lock);
shca->ofdev = dev;
shca->ipz_hca_handle.handle = *handle;
@@ -934,7 +937,7 @@ void ehca_poll_eqs(unsigned long data)
ehca_process_eq(shca, 0);
}
}
- mod_timer(&poll_eqs_timer, jiffies + HZ);
+ mod_timer(&poll_eqs_timer, round_jiffies(jiffies + HZ));
spin_unlock(&shca_list_lock);
}
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_qp.c b/trunk/drivers/infiniband/hw/ehca/ehca_qp.c
index eff5fb55604b..1012f15a7140 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_qp.c
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -592,10 +592,8 @@ static struct ehca_qp *internal_create_qp(
goto create_qp_exit1;
}
- if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
- parms.sigtype = HCALL_SIGT_EVERY;
- else
- parms.sigtype = HCALL_SIGT_BY_WQE;
+ /* Always signal by WQE so we can hide circ. WQEs */
+ parms.sigtype = HCALL_SIGT_BY_WQE;
/* UD_AV CIRCUMVENTION */
max_send_sge = init_attr->cap.max_send_sge;
@@ -618,6 +616,10 @@ static struct ehca_qp *internal_create_qp(
parms.squeue.max_sge = max_send_sge;
parms.rqueue.max_sge = max_recv_sge;
+ /* RC QPs need one more SWQE for unsolicited ack circumvention */
+ if (qp_type == IB_QPT_RC)
+ parms.squeue.max_wr++;
+
if (EHCA_BMASK_GET(HCA_CAP_MINI_QP, shca->hca_cap)) {
if (HAS_SQ(my_qp))
ehca_determine_small_queue(
@@ -650,6 +652,8 @@ static struct ehca_qp *internal_create_qp(
parms.squeue.act_nr_sges = 1;
parms.rqueue.act_nr_sges = 1;
}
+ /* hide the extra WQE */
+ parms.squeue.act_nr_wqes--;
break;
case IB_QPT_UD:
case IB_QPT_GSI:
@@ -729,12 +733,31 @@ static struct ehca_qp *internal_create_qp(
init_attr->cap.max_send_wr = parms.squeue.act_nr_wqes;
my_qp->init_attr = *init_attr;
+ if (qp_type == IB_QPT_SMI || qp_type == IB_QPT_GSI) {
+ shca->sport[init_attr->port_num - 1].ibqp_sqp[qp_type] =
+ &my_qp->ib_qp;
+ if (ehca_nr_ports < 0) {
+ /* alloc array to cache subsequent modify qp parms
+ * for autodetect mode
+ */
+ my_qp->mod_qp_parm =
+ kzalloc(EHCA_MOD_QP_PARM_MAX *
+ sizeof(*my_qp->mod_qp_parm),
+ GFP_KERNEL);
+ if (!my_qp->mod_qp_parm) {
+ ehca_err(pd->device,
+ "Could not alloc mod_qp_parm");
+ goto create_qp_exit4;
+ }
+ }
+ }
+
/* NOTE: define_apq0() not supported yet */
if (qp_type == IB_QPT_GSI) {
h_ret = ehca_define_sqp(shca, my_qp, init_attr);
if (h_ret != H_SUCCESS) {
ret = ehca2ib_return_code(h_ret);
- goto create_qp_exit4;
+ goto create_qp_exit5;
}
}
@@ -743,7 +766,7 @@ static struct ehca_qp *internal_create_qp(
if (ret) {
ehca_err(pd->device,
"Couldn't assign qp to send_cq ret=%i", ret);
- goto create_qp_exit4;
+ goto create_qp_exit5;
}
}
@@ -769,12 +792,18 @@ static struct ehca_qp *internal_create_qp(
if (ib_copy_to_udata(udata, &resp, sizeof resp)) {
ehca_err(pd->device, "Copy to udata failed");
ret = -EINVAL;
- goto create_qp_exit4;
+ goto create_qp_exit6;
}
}
return my_qp;
+create_qp_exit6:
+ ehca_cq_unassign_qp(my_qp->send_cq, my_qp->real_qp_num);
+
+create_qp_exit5:
+ kfree(my_qp->mod_qp_parm);
+
create_qp_exit4:
if (HAS_RQ(my_qp))
ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue);
@@ -858,7 +887,7 @@ struct ib_srq *ehca_create_srq(struct ib_pd *pd,
update_mask,
mqpcb, my_qp->galpas.kernel);
if (hret != H_SUCCESS) {
- ehca_err(pd->device, "Could not modify SRQ to INIT"
+ ehca_err(pd->device, "Could not modify SRQ to INIT "
"ehca_qp=%p qp_num=%x h_ret=%li",
my_qp, my_qp->real_qp_num, hret);
goto create_srq2;
@@ -872,7 +901,7 @@ struct ib_srq *ehca_create_srq(struct ib_pd *pd,
update_mask,
mqpcb, my_qp->galpas.kernel);
if (hret != H_SUCCESS) {
- ehca_err(pd->device, "Could not enable SRQ"
+ ehca_err(pd->device, "Could not enable SRQ "
"ehca_qp=%p qp_num=%x h_ret=%li",
my_qp, my_qp->real_qp_num, hret);
goto create_srq2;
@@ -886,7 +915,7 @@ struct ib_srq *ehca_create_srq(struct ib_pd *pd,
update_mask,
mqpcb, my_qp->galpas.kernel);
if (hret != H_SUCCESS) {
- ehca_err(pd->device, "Could not modify SRQ to RTR"
+ ehca_err(pd->device, "Could not modify SRQ to RTR "
"ehca_qp=%p qp_num=%x h_ret=%li",
my_qp, my_qp->real_qp_num, hret);
goto create_srq2;
@@ -992,7 +1021,7 @@ static int internal_modify_qp(struct ib_qp *ibqp,
unsigned long flags = 0;
/* do query_qp to obtain current attr values */
- mqpcb = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
+ mqpcb = ehca_alloc_fw_ctrlblock(GFP_ATOMIC);
if (!mqpcb) {
ehca_err(ibqp->device, "Could not get zeroed page for mqpcb "
"ehca_qp=%p qp_num=%x ", my_qp, ibqp->qp_num);
@@ -1180,6 +1209,8 @@ static int internal_modify_qp(struct ib_qp *ibqp,
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PRIM_P_KEY_IDX, 1);
}
if (attr_mask & IB_QP_PORT) {
+ struct ehca_sport *sport;
+ struct ehca_qp *aqp1;
if (attr->port_num < 1 || attr->port_num > shca->num_ports) {
ret = -EINVAL;
ehca_err(ibqp->device, "Invalid port=%x. "
@@ -1188,6 +1219,29 @@ static int internal_modify_qp(struct ib_qp *ibqp,
shca->num_ports);
goto modify_qp_exit2;
}
+ sport = &shca->sport[attr->port_num - 1];
+ if (!sport->ibqp_sqp[IB_QPT_GSI]) {
+ /* should not occur */
+ ret = -EFAULT;
+ ehca_err(ibqp->device, "AQP1 was not created for "
+ "port=%x", attr->port_num);
+ goto modify_qp_exit2;
+ }
+ aqp1 = container_of(sport->ibqp_sqp[IB_QPT_GSI],
+ struct ehca_qp, ib_qp);
+ if (ibqp->qp_type != IB_QPT_GSI &&
+ ibqp->qp_type != IB_QPT_SMI &&
+ aqp1->mod_qp_parm) {
+ /*
+ * firmware will reject this modify_qp() because
+ * port is not activated/initialized fully
+ */
+ ret = -EFAULT;
+ ehca_warn(ibqp->device, "Couldn't modify qp port=%x: "
+ "either port is being activated (try again) "
+ "or cabling issue", attr->port_num);
+ goto modify_qp_exit2;
+ }
mqpcb->prim_phys_port = attr->port_num;
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PRIM_PHYS_PORT, 1);
}
@@ -1244,6 +1298,8 @@ static int internal_modify_qp(struct ib_qp *ibqp,
}
if (attr_mask & IB_QP_PATH_MTU) {
+ /* store ld(MTU) */
+ my_qp->mtu_shift = attr->path_mtu + 7;
mqpcb->path_mtu = attr->path_mtu;
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PATH_MTU, 1);
}
@@ -1467,6 +1523,8 @@ static int internal_modify_qp(struct ib_qp *ibqp,
int ehca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask,
struct ib_udata *udata)
{
+ struct ehca_shca *shca = container_of(ibqp->device, struct ehca_shca,
+ ib_device);
struct ehca_qp *my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
struct ehca_pd *my_pd = container_of(my_qp->ib_qp.pd, struct ehca_pd,
ib_pd);
@@ -1479,9 +1537,100 @@ int ehca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask,
return -EINVAL;
}
+ /* The if-block below caches qp_attr to be modified for GSI and SMI
+ * qps during the initialization by ib_mad. When the respective port
+ * is activated, ie we got an event PORT_ACTIVE, we'll replay the
+ * cached modify calls sequence, see ehca_recover_sqs() below.
+ * Why that is required:
+ * 1) If one port is connected, older code requires that port one
+ * to be connected and module option nr_ports=1 to be given by
+ * user, which is very inconvenient for end user.
+ * 2) Firmware accepts modify_qp() only if respective port has become
+ * active. Older code had a wait loop of 30sec create_qp()/
+ * define_aqp1(), which is not appropriate in practice. This
+ * code now removes that wait loop, see define_aqp1(), and always
+ * reports all ports to ib_mad resp. users. Only activated ports
+ * will then usable for the users.
+ */
+ if (ibqp->qp_type == IB_QPT_GSI || ibqp->qp_type == IB_QPT_SMI) {
+ int port = my_qp->init_attr.port_num;
+ struct ehca_sport *sport = &shca->sport[port - 1];
+ unsigned long flags;
+ spin_lock_irqsave(&sport->mod_sqp_lock, flags);
+ /* cache qp_attr only during init */
+ if (my_qp->mod_qp_parm) {
+ struct ehca_mod_qp_parm *p;
+ if (my_qp->mod_qp_parm_idx >= EHCA_MOD_QP_PARM_MAX) {
+ ehca_err(&shca->ib_device,
+ "mod_qp_parm overflow state=%x port=%x"
+ " type=%x", attr->qp_state,
+ my_qp->init_attr.port_num,
+ ibqp->qp_type);
+ spin_unlock_irqrestore(&sport->mod_sqp_lock,
+ flags);
+ return -EINVAL;
+ }
+ p = &my_qp->mod_qp_parm[my_qp->mod_qp_parm_idx];
+ p->mask = attr_mask;
+ p->attr = *attr;
+ my_qp->mod_qp_parm_idx++;
+ ehca_dbg(&shca->ib_device,
+ "Saved qp_attr for state=%x port=%x type=%x",
+ attr->qp_state, my_qp->init_attr.port_num,
+ ibqp->qp_type);
+ spin_unlock_irqrestore(&sport->mod_sqp_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&sport->mod_sqp_lock, flags);
+ }
+
return internal_modify_qp(ibqp, attr, attr_mask, 0);
}
+void ehca_recover_sqp(struct ib_qp *sqp)
+{
+ struct ehca_qp *my_sqp = container_of(sqp, struct ehca_qp, ib_qp);
+ int port = my_sqp->init_attr.port_num;
+ struct ib_qp_attr attr;
+ struct ehca_mod_qp_parm *qp_parm;
+ int i, qp_parm_idx, ret;
+ unsigned long flags, wr_cnt;
+
+ if (!my_sqp->mod_qp_parm)
+ return;
+ ehca_dbg(sqp->device, "SQP port=%x qp_num=%x", port, sqp->qp_num);
+
+ qp_parm = my_sqp->mod_qp_parm;
+ qp_parm_idx = my_sqp->mod_qp_parm_idx;
+ for (i = 0; i < qp_parm_idx; i++) {
+ attr = qp_parm[i].attr;
+ ret = internal_modify_qp(sqp, &attr, qp_parm[i].mask, 0);
+ if (ret) {
+ ehca_err(sqp->device, "Could not modify SQP port=%x "
+ "qp_num=%x ret=%x", port, sqp->qp_num, ret);
+ goto free_qp_parm;
+ }
+ ehca_dbg(sqp->device, "SQP port=%x qp_num=%x in state=%x",
+ port, sqp->qp_num, attr.qp_state);
+ }
+
+ /* re-trigger posted recv wrs */
+ wr_cnt = my_sqp->ipz_rqueue.current_q_offset /
+ my_sqp->ipz_rqueue.qe_size;
+ if (wr_cnt) {
+ spin_lock_irqsave(&my_sqp->spinlock_r, flags);
+ hipz_update_rqa(my_sqp, wr_cnt);
+ spin_unlock_irqrestore(&my_sqp->spinlock_r, flags);
+ ehca_dbg(sqp->device, "doorbell port=%x qp_num=%x wr_cnt=%lx",
+ port, sqp->qp_num, wr_cnt);
+ }
+
+free_qp_parm:
+ kfree(qp_parm);
+ /* this prevents subsequent calls to modify_qp() to cache qp_attr */
+ my_sqp->mod_qp_parm = NULL;
+}
+
int ehca_query_qp(struct ib_qp *qp,
struct ib_qp_attr *qp_attr,
int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr)
@@ -1769,6 +1918,7 @@ static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp,
struct ehca_shca *shca = container_of(dev, struct ehca_shca, ib_device);
struct ehca_pd *my_pd = container_of(my_qp->ib_qp.pd, struct ehca_pd,
ib_pd);
+ struct ehca_sport *sport = &shca->sport[my_qp->init_attr.port_num - 1];
u32 cur_pid = current->tgid;
u32 qp_num = my_qp->real_qp_num;
int ret;
@@ -1815,6 +1965,14 @@ static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp,
port_num = my_qp->init_attr.port_num;
qp_type = my_qp->init_attr.qp_type;
+ if (qp_type == IB_QPT_SMI || qp_type == IB_QPT_GSI) {
+ spin_lock_irqsave(&sport->mod_sqp_lock, flags);
+ kfree(my_qp->mod_qp_parm);
+ my_qp->mod_qp_parm = NULL;
+ shca->sport[port_num - 1].ibqp_sqp[qp_type] = NULL;
+ spin_unlock_irqrestore(&sport->mod_sqp_lock, flags);
+ }
+
/* no support for IB_QPT_SMI yet */
if (qp_type == IB_QPT_GSI) {
struct ib_event event;
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_reqs.c b/trunk/drivers/infiniband/hw/ehca/ehca_reqs.c
index ea91360835d3..3aacc8cf1e44 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_reqs.c
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_reqs.c
@@ -50,6 +50,9 @@
#include "hcp_if.h"
#include "hipz_fns.h"
+/* in RC traffic, insert an empty RDMA READ every this many packets */
+#define ACK_CIRC_THRESHOLD 2000000
+
static inline int ehca_write_rwqe(struct ipz_queue *ipz_rqueue,
struct ehca_wqe *wqe_p,
struct ib_recv_wr *recv_wr)
@@ -81,7 +84,7 @@ static inline int ehca_write_rwqe(struct ipz_queue *ipz_rqueue,
if (ehca_debug_level) {
ehca_gen_dbg("RECEIVE WQE written into ipz_rqueue=%p",
ipz_rqueue);
- ehca_dmp( wqe_p, 16*(6 + wqe_p->nr_of_data_seg), "recv wqe");
+ ehca_dmp(wqe_p, 16*(6 + wqe_p->nr_of_data_seg), "recv wqe");
}
return 0;
@@ -135,7 +138,8 @@ static void trace_send_wr_ud(const struct ib_send_wr *send_wr)
static inline int ehca_write_swqe(struct ehca_qp *qp,
struct ehca_wqe *wqe_p,
- const struct ib_send_wr *send_wr)
+ const struct ib_send_wr *send_wr,
+ int hidden)
{
u32 idx;
u64 dma_length;
@@ -176,7 +180,9 @@ static inline int ehca_write_swqe(struct ehca_qp *qp,
wqe_p->wr_flag = 0;
- if (send_wr->send_flags & IB_SEND_SIGNALED)
+ if ((send_wr->send_flags & IB_SEND_SIGNALED ||
+ qp->init_attr.sq_sig_type == IB_SIGNAL_ALL_WR)
+ && !hidden)
wqe_p->wr_flag |= WQE_WRFLAG_REQ_SIGNAL_COM;
if (send_wr->opcode == IB_WR_SEND_WITH_IMM ||
@@ -199,7 +205,7 @@ static inline int ehca_write_swqe(struct ehca_qp *qp,
wqe_p->destination_qp_number = send_wr->wr.ud.remote_qpn << 8;
wqe_p->local_ee_context_qkey = remote_qkey;
- if (!send_wr->wr.ud.ah) {
+ if (unlikely(!send_wr->wr.ud.ah)) {
ehca_gen_err("wr.ud.ah is NULL. qp=%p", qp);
return -EINVAL;
}
@@ -255,6 +261,15 @@ static inline int ehca_write_swqe(struct ehca_qp *qp,
} /* eof idx */
wqe_p->u.nud.atomic_1st_op_dma_len = dma_length;
+ /* unsolicited ack circumvention */
+ if (send_wr->opcode == IB_WR_RDMA_READ) {
+ /* on RDMA read, switch on and reset counters */
+ qp->message_count = qp->packet_count = 0;
+ qp->unsol_ack_circ = 1;
+ } else
+ /* else estimate #packets */
+ qp->packet_count += (dma_length >> qp->mtu_shift) + 1;
+
break;
default:
@@ -355,13 +370,49 @@ static inline void map_ib_wc_status(u32 cqe_status,
*wc_status = IB_WC_SUCCESS;
}
+static inline int post_one_send(struct ehca_qp *my_qp,
+ struct ib_send_wr *cur_send_wr,
+ struct ib_send_wr **bad_send_wr,
+ int hidden)
+{
+ struct ehca_wqe *wqe_p;
+ int ret;
+ u64 start_offset = my_qp->ipz_squeue.current_q_offset;
+
+ /* get pointer next to free WQE */
+ wqe_p = ipz_qeit_get_inc(&my_qp->ipz_squeue);
+ if (unlikely(!wqe_p)) {
+ /* too many posted work requests: queue overflow */
+ if (bad_send_wr)
+ *bad_send_wr = cur_send_wr;
+ ehca_err(my_qp->ib_qp.device, "Too many posted WQEs "
+ "qp_num=%x", my_qp->ib_qp.qp_num);
+ return -ENOMEM;
+ }
+ /* write a SEND WQE into the QUEUE */
+ ret = ehca_write_swqe(my_qp, wqe_p, cur_send_wr, hidden);
+ /*
+ * if something failed,
+ * reset the free entry pointer to the start value
+ */
+ if (unlikely(ret)) {
+ my_qp->ipz_squeue.current_q_offset = start_offset;
+ if (bad_send_wr)
+ *bad_send_wr = cur_send_wr;
+ ehca_err(my_qp->ib_qp.device, "Could not write WQE "
+ "qp_num=%x", my_qp->ib_qp.qp_num);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
int ehca_post_send(struct ib_qp *qp,
struct ib_send_wr *send_wr,
struct ib_send_wr **bad_send_wr)
{
struct ehca_qp *my_qp = container_of(qp, struct ehca_qp, ib_qp);
struct ib_send_wr *cur_send_wr;
- struct ehca_wqe *wqe_p;
int wqe_cnt = 0;
int ret = 0;
unsigned long flags;
@@ -369,37 +420,33 @@ int ehca_post_send(struct ib_qp *qp,
/* LOCK the QUEUE */
spin_lock_irqsave(&my_qp->spinlock_s, flags);
+ /* Send an empty extra RDMA read if:
+ * 1) there has been an RDMA read on this connection before
+ * 2) no RDMA read occurred for ACK_CIRC_THRESHOLD link packets
+ * 3) we can be sure that any previous extra RDMA read has been
+ * processed so we don't overflow the SQ
+ */
+ if (unlikely(my_qp->unsol_ack_circ &&
+ my_qp->packet_count > ACK_CIRC_THRESHOLD &&
+ my_qp->message_count > my_qp->init_attr.cap.max_send_wr)) {
+ /* insert an empty RDMA READ to fix up the remote QP state */
+ struct ib_send_wr circ_wr;
+ memset(&circ_wr, 0, sizeof(circ_wr));
+ circ_wr.opcode = IB_WR_RDMA_READ;
+ post_one_send(my_qp, &circ_wr, NULL, 1); /* ignore retcode */
+ wqe_cnt++;
+ ehca_dbg(qp->device, "posted circ wr qp_num=%x", qp->qp_num);
+ my_qp->message_count = my_qp->packet_count = 0;
+ }
+
/* loop processes list of send reqs */
for (cur_send_wr = send_wr; cur_send_wr != NULL;
cur_send_wr = cur_send_wr->next) {
- u64 start_offset = my_qp->ipz_squeue.current_q_offset;
- /* get pointer next to free WQE */
- wqe_p = ipz_qeit_get_inc(&my_qp->ipz_squeue);
- if (unlikely(!wqe_p)) {
- /* too many posted work requests: queue overflow */
- if (bad_send_wr)
- *bad_send_wr = cur_send_wr;
- if (wqe_cnt == 0) {
- ret = -ENOMEM;
- ehca_err(qp->device, "Too many posted WQEs "
- "qp_num=%x", qp->qp_num);
- }
- goto post_send_exit0;
- }
- /* write a SEND WQE into the QUEUE */
- ret = ehca_write_swqe(my_qp, wqe_p, cur_send_wr);
- /*
- * if something failed,
- * reset the free entry pointer to the start value
- */
+ ret = post_one_send(my_qp, cur_send_wr, bad_send_wr, 0);
if (unlikely(ret)) {
- my_qp->ipz_squeue.current_q_offset = start_offset;
- *bad_send_wr = cur_send_wr;
- if (wqe_cnt == 0) {
- ret = -EINVAL;
- ehca_err(qp->device, "Could not write WQE "
- "qp_num=%x", qp->qp_num);
- }
+ /* if one or more WQEs were successful, don't fail */
+ if (wqe_cnt)
+ ret = 0;
goto post_send_exit0;
}
wqe_cnt++;
@@ -410,6 +457,7 @@ int ehca_post_send(struct ib_qp *qp,
post_send_exit0:
iosync(); /* serialize GAL register access */
hipz_update_sqa(my_qp, wqe_cnt);
+ my_qp->message_count += wqe_cnt;
spin_unlock_irqrestore(&my_qp->spinlock_s, flags);
return ret;
}
diff --git a/trunk/drivers/infiniband/hw/ehca/ehca_sqp.c b/trunk/drivers/infiniband/hw/ehca/ehca_sqp.c
index f0792e5fbd02..79e72b25b252 100644
--- a/trunk/drivers/infiniband/hw/ehca/ehca_sqp.c
+++ b/trunk/drivers/infiniband/hw/ehca/ehca_sqp.c
@@ -40,11 +40,8 @@
*/
-#include
-#include
#include "ehca_classes.h"
#include "ehca_tools.h"
-#include "ehca_qes.h"
#include "ehca_iverbs.h"
#include "hcp_if.h"
@@ -93,6 +90,9 @@ u64 ehca_define_sqp(struct ehca_shca *shca,
return H_PARAMETER;
}
+ if (ehca_nr_ports < 0) /* autodetect mode */
+ return H_SUCCESS;
+
for (counter = 0;
shca->sport[port - 1].port_state != IB_PORT_ACTIVE &&
counter < ehca_port_act_time;
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_common.h b/trunk/drivers/infiniband/hw/ipath/ipath_common.h
index 851df8a75e79..414621095540 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_common.h
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_common.h
@@ -81,6 +81,16 @@
#define IPATH_IB_LINK_LOOPBACK 6 /* enable local loopback */
#define IPATH_IB_LINK_EXTERNAL 7 /* normal, disable local loopback */
+/*
+ * These 3 values (SDR and DDR may be ORed for auto-speed
+ * negotiation) are used for the 3rd argument to path_f_set_ib_cfg
+ * with cmd IPATH_IB_CFG_SPD_ENB, by direct calls or via sysfs. They
+ * are also the the possible values for ipath_link_speed_enabled and active
+ * The values were chosen to match values used within the IB spec.
+ */
+#define IPATH_IB_SDR 1
+#define IPATH_IB_DDR 2
+
/*
* stats maintained by the driver. For now, at least, this is global
* to all minor devices.
@@ -433,8 +443,9 @@ struct ipath_user_info {
#define IPATH_CMD_UNUSED_2 26
#define IPATH_CMD_PIOAVAILUPD 27 /* force an update of PIOAvail reg */
#define IPATH_CMD_POLL_TYPE 28 /* set the kind of polling we want */
+#define IPATH_CMD_ARMLAUNCH_CTRL 29 /* armlaunch detection control */
-#define IPATH_CMD_MAX 28
+#define IPATH_CMD_MAX 29
/*
* Poll types
@@ -477,6 +488,8 @@ struct ipath_cmd {
__u64 port_info;
/* enable/disable receipt of packets */
__u32 recv_ctrl;
+ /* enable/disable armlaunch errors (non-zero to enable) */
+ __u32 armlaunch_ctrl;
/* partition key to set */
__u16 part_key;
/* user address of __u32 bitmask of active slaves */
@@ -579,7 +592,7 @@ struct ipath_flash {
struct infinipath_counters {
__u64 LBIntCnt;
__u64 LBFlowStallCnt;
- __u64 Reserved1;
+ __u64 TxSDmaDescCnt; /* was Reserved1 */
__u64 TxUnsupVLErrCnt;
__u64 TxDataPktCnt;
__u64 TxFlowPktCnt;
@@ -615,12 +628,26 @@ struct infinipath_counters {
__u64 RxP6HdrEgrOvflCnt;
__u64 RxP7HdrEgrOvflCnt;
__u64 RxP8HdrEgrOvflCnt;
- __u64 Reserved6;
- __u64 Reserved7;
+ __u64 RxP9HdrEgrOvflCnt; /* was Reserved6 */
+ __u64 RxP10HdrEgrOvflCnt; /* was Reserved7 */
+ __u64 RxP11HdrEgrOvflCnt; /* new for IBA7220 */
+ __u64 RxP12HdrEgrOvflCnt; /* new for IBA7220 */
+ __u64 RxP13HdrEgrOvflCnt; /* new for IBA7220 */
+ __u64 RxP14HdrEgrOvflCnt; /* new for IBA7220 */
+ __u64 RxP15HdrEgrOvflCnt; /* new for IBA7220 */
+ __u64 RxP16HdrEgrOvflCnt; /* new for IBA7220 */
__u64 IBStatusChangeCnt;
__u64 IBLinkErrRecoveryCnt;
__u64 IBLinkDownedCnt;
__u64 IBSymbolErrCnt;
+ /* The following are new for IBA7220 */
+ __u64 RxVL15DroppedPktCnt;
+ __u64 RxOtherLocalPhyErrCnt;
+ __u64 PcieRetryBufDiagQwordCnt;
+ __u64 ExcessBufferOvflCnt;
+ __u64 LocalLinkIntegrityErrCnt;
+ __u64 RxVlErrCnt;
+ __u64 RxDlidFltrCnt;
};
/*
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_cq.c b/trunk/drivers/infiniband/hw/ipath/ipath_cq.c
index d1380c7a1703..a03bd28d9b48 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_cq.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_cq.c
@@ -421,7 +421,7 @@ int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
else
n = head - tail;
if (unlikely((u32)cqe < n)) {
- ret = -EOVERFLOW;
+ ret = -EINVAL;
goto bail_unlock;
}
for (n = 0; tail != head; n++) {
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_debug.h b/trunk/drivers/infiniband/hw/ipath/ipath_debug.h
index 19c56e6491eb..d6f69532d83f 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_debug.h
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_debug.h
@@ -55,7 +55,7 @@
#define __IPATH_PKTDBG 0x80 /* print packet data */
/* print process startup (init)/exit messages */
#define __IPATH_PROCDBG 0x100
-/* print mmap/nopage stuff, not using VDBG any more */
+/* print mmap/fault stuff, not using VDBG any more */
#define __IPATH_MMDBG 0x200
#define __IPATH_ERRPKTDBG 0x400
#define __IPATH_USER_SEND 0x1000 /* use user mode send */
@@ -81,7 +81,7 @@
#define __IPATH_VERBDBG 0x0 /* very verbose debug */
#define __IPATH_PKTDBG 0x0 /* print packet data */
#define __IPATH_PROCDBG 0x0 /* process startup (init)/exit messages */
-/* print mmap/nopage stuff, not using VDBG any more */
+/* print mmap/fault stuff, not using VDBG any more */
#define __IPATH_MMDBG 0x0
#define __IPATH_EPKTDBG 0x0 /* print ethernet packet data */
#define __IPATH_IPATHDBG 0x0 /* Ethernet (IPATH) table dump on */
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_driver.c b/trunk/drivers/infiniband/hw/ipath/ipath_driver.c
index fc355981bbab..d5ff6ca2db30 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -334,6 +334,8 @@ static void ipath_verify_pioperf(struct ipath_devdata *dd)
udelay(1);
}
+ ipath_disable_armlaunch(dd);
+
writeq(0, piobuf); /* length 0, no dwords actually sent */
ipath_flush_wc();
@@ -365,6 +367,7 @@ static void ipath_verify_pioperf(struct ipath_devdata *dd)
done:
/* disarm piobuf, so it's available again */
ipath_disarm_piobufs(dd, pbnum, 1);
+ ipath_enable_armlaunch(dd);
}
static int __devinit ipath_init_one(struct pci_dev *pdev,
@@ -803,31 +806,37 @@ void ipath_disarm_piobufs(struct ipath_devdata *dd, unsigned first,
unsigned cnt)
{
unsigned i, last = first + cnt;
- u64 sendctrl, sendorig;
+ unsigned long flags;
ipath_cdbg(PKT, "disarm %u PIObufs first=%u\n", cnt, first);
- sendorig = dd->ipath_sendctrl;
for (i = first; i < last; i++) {
- sendctrl = sendorig | INFINIPATH_S_DISARM |
- (i << INFINIPATH_S_DISARMPIOBUF_SHIFT);
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+ /*
+ * The disarm-related bits are write-only, so it
+ * is ok to OR them in with our copy of sendctrl
+ * while we hold the lock.
+ */
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
- sendctrl);
+ dd->ipath_sendctrl | INFINIPATH_S_DISARM |
+ (i << INFINIPATH_S_DISARMPIOBUF_SHIFT));
+ /* can't disarm bufs back-to-back per iba7220 spec */
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
}
/*
- * Write it again with current value, in case ipath_sendctrl changed
- * while we were looping; no critical bits that would require
- * locking.
- *
- * disable PIOAVAILUPD, then re-enable, reading scratch in
+ * Disable PIOAVAILUPD, then re-enable, reading scratch in
* between. This seems to avoid a chip timing race that causes
- * pioavail updates to memory to stop.
+ * pioavail updates to memory to stop. We xor as we don't
+ * know the state of the bit when we're called.
*/
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
- sendorig & ~INFINIPATH_S_PIOBUFAVAILUPD);
- sendorig = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ dd->ipath_sendctrl ^ INFINIPATH_S_PIOBUFAVAILUPD);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
dd->ipath_sendctrl);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
}
/**
@@ -1003,12 +1012,10 @@ static void get_rhf_errstring(u32 err, char *msg, size_t len)
* ipath_get_egrbuf - get an eager buffer
* @dd: the infinipath device
* @bufnum: the eager buffer to get
- * @err: unused
*
* must only be called if ipath_pd[port] is known to be allocated
*/
-static inline void *ipath_get_egrbuf(struct ipath_devdata *dd, u32 bufnum,
- int err)
+static inline void *ipath_get_egrbuf(struct ipath_devdata *dd, u32 bufnum)
{
return dd->ipath_port0_skbinfo ?
(void *) dd->ipath_port0_skbinfo[bufnum].skb->data : NULL;
@@ -1100,13 +1107,14 @@ static void ipath_rcv_hdrerr(struct ipath_devdata *dd,
/*
* ipath_kreceive - receive a packet
- * @dd: the infinipath device
+ * @pd: the infinipath port
*
* called from interrupt handler for errors or receive interrupt
*/
-void ipath_kreceive(struct ipath_devdata *dd)
+void ipath_kreceive(struct ipath_portdata *pd)
{
u64 *rc;
+ struct ipath_devdata *dd = pd->port_dd;
void *ebuf;
const u32 rsize = dd->ipath_rcvhdrentsize; /* words */
const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */
@@ -1121,8 +1129,8 @@ void ipath_kreceive(struct ipath_devdata *dd)
goto bail;
}
- l = dd->ipath_port0head;
- hdrqtail = (u32) le64_to_cpu(*dd->ipath_hdrqtailptr);
+ l = pd->port_head;
+ hdrqtail = ipath_get_rcvhdrtail(pd);
if (l == hdrqtail)
goto bail;
@@ -1131,7 +1139,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
u32 qp;
u8 *bthbytes;
- rc = (u64 *) (dd->ipath_pd[0]->port_rcvhdrq + (l << 2));
+ rc = (u64 *) (pd->port_rcvhdrq + (l << 2));
hdr = (struct ipath_message_header *)&rc[1];
/*
* could make a network order version of IPATH_KD_QP, and
@@ -1156,7 +1164,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
etail = ipath_hdrget_index((__le32 *) rc);
if (tlen > sizeof(*hdr) ||
etype == RCVHQ_RCV_TYPE_NON_KD)
- ebuf = ipath_get_egrbuf(dd, etail, 0);
+ ebuf = ipath_get_egrbuf(dd, etail);
}
/*
@@ -1191,7 +1199,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
be32_to_cpu(hdr->bth[0]) & 0xff);
else {
/*
- * error packet, type of error unknown.
+ * error packet, type of error unknown.
* Probably type 3, but we don't know, so don't
* even try to print the opcode, etc.
*/
@@ -1241,7 +1249,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
* earlier packets, we "almost" guarantee we have covered
* that case.
*/
- u32 hqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
+ u32 hqtail = ipath_get_rcvhdrtail(pd);
if (hqtail != hdrqtail) {
hdrqtail = hqtail;
reloop = 1; /* loop 1 extra time at most */
@@ -1251,7 +1259,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
pkttot += i;
- dd->ipath_port0head = l;
+ pd->port_head = l;
if (pkttot > ipath_stats.sps_maxpkts_call)
ipath_stats.sps_maxpkts_call = pkttot;
@@ -1335,14 +1343,9 @@ static void ipath_update_pio_bufs(struct ipath_devdata *dd)
/*
* Chip Errata: bug 6641; even and odd qwords>3 are swapped
*/
- if (i > 3) {
- if (i & 1)
- piov = le64_to_cpu(
- dd->ipath_pioavailregs_dma[i - 1]);
- else
- piov = le64_to_cpu(
- dd->ipath_pioavailregs_dma[i + 1]);
- } else
+ if (i > 3 && (dd->ipath_flags & IPATH_SWAP_PIOBUFS))
+ piov = le64_to_cpu(dd->ipath_pioavailregs_dma[i ^ 1]);
+ else
piov = le64_to_cpu(dd->ipath_pioavailregs_dma[i]);
pchg = _IPATH_ALL_CHECKBITS &
~(dd->ipath_pioavailshadow[i] ^ piov);
@@ -1601,7 +1604,8 @@ int ipath_create_rcvhdrq(struct ipath_devdata *dd,
/* clear for security and sanity on each use */
memset(pd->port_rcvhdrq, 0, pd->port_rcvhdrq_size);
- memset(pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE);
+ if (pd->port_rcvhdrtail_kvaddr)
+ memset(pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE);
/*
* tell chip each time we init it, even if we are re-using previous
@@ -1617,77 +1621,6 @@ int ipath_create_rcvhdrq(struct ipath_devdata *dd,
return ret;
}
-int ipath_waitfor_complete(struct ipath_devdata *dd, ipath_kreg reg_id,
- u64 bits_to_wait_for, u64 * valp)
-{
- unsigned long timeout;
- u64 lastval, val;
- int ret;
-
- lastval = ipath_read_kreg64(dd, reg_id);
- /* wait a ridiculously long time */
- timeout = jiffies + msecs_to_jiffies(5);
- do {
- val = ipath_read_kreg64(dd, reg_id);
- /* set so they have something, even on failures. */
- *valp = val;
- if ((val & bits_to_wait_for) == bits_to_wait_for) {
- ret = 0;
- break;
- }
- if (val != lastval)
- ipath_cdbg(VERBOSE, "Changed from %llx to %llx, "
- "waiting for %llx bits\n",
- (unsigned long long) lastval,
- (unsigned long long) val,
- (unsigned long long) bits_to_wait_for);
- cond_resched();
- if (time_after(jiffies, timeout)) {
- ipath_dbg("Didn't get bits %llx in register 0x%x, "
- "got %llx\n",
- (unsigned long long) bits_to_wait_for,
- reg_id, (unsigned long long) *valp);
- ret = -ENODEV;
- break;
- }
- } while (1);
-
- return ret;
-}
-
-/**
- * ipath_waitfor_mdio_cmdready - wait for last command to complete
- * @dd: the infinipath device
- *
- * Like ipath_waitfor_complete(), but we wait for the CMDVALID bit to go
- * away indicating the last command has completed. It doesn't return data
- */
-int ipath_waitfor_mdio_cmdready(struct ipath_devdata *dd)
-{
- unsigned long timeout;
- u64 val;
- int ret;
-
- /* wait a ridiculously long time */
- timeout = jiffies + msecs_to_jiffies(5);
- do {
- val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_mdio);
- if (!(val & IPATH_MDIO_CMDVALID)) {
- ret = 0;
- break;
- }
- cond_resched();
- if (time_after(jiffies, timeout)) {
- ipath_dbg("CMDVALID stuck in mdio reg? (%llx)\n",
- (unsigned long long) val);
- ret = -ENODEV;
- break;
- }
- } while (1);
-
- return ret;
-}
-
/*
* Flush all sends that might be in the ready to send state, as well as any
@@ -2056,6 +1989,8 @@ void ipath_set_led_override(struct ipath_devdata *dd, unsigned int val)
*/
void ipath_shutdown_device(struct ipath_devdata *dd)
{
+ unsigned long flags;
+
ipath_dbg("Shutting down the device\n");
dd->ipath_flags |= IPATH_LINKUNK;
@@ -2076,9 +2011,13 @@ void ipath_shutdown_device(struct ipath_devdata *dd)
* gracefully stop all sends allowing any in progress to trickle out
* first.
*/
- ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, 0ULL);
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+ dd->ipath_sendctrl = 0;
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
/* flush it */
ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
+
/*
* enough for anything that's going to trickle out to have actually
* done so.
@@ -2335,5 +2274,34 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv)
}
return 0;
}
+
+/*
+ * Disable and enable the armlaunch error. Used for PIO bandwidth testing on
+ * the 7220, which is count-based, rather than trigger-based. Safe for the
+ * driver check, since it's at init. Not completely safe when used for
+ * user-mode checking, since some error checking can be lost, but not
+ * particularly risky, and only has problematic side-effects in the face of
+ * very buggy user code. There is no reference counting, but that's also
+ * fine, given the intended use.
+ */
+void ipath_enable_armlaunch(struct ipath_devdata *dd)
+{
+ dd->ipath_lasterror &= ~INFINIPATH_E_SPIOARMLAUNCH;
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear,
+ INFINIPATH_E_SPIOARMLAUNCH);
+ dd->ipath_errormask |= INFINIPATH_E_SPIOARMLAUNCH;
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
+ dd->ipath_errormask);
+}
+
+void ipath_disable_armlaunch(struct ipath_devdata *dd)
+{
+ /* so don't re-enable if already set */
+ dd->ipath_maskederrs &= ~INFINIPATH_E_SPIOARMLAUNCH;
+ dd->ipath_errormask &= ~INFINIPATH_E_SPIOARMLAUNCH;
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
+ dd->ipath_errormask);
+}
+
module_init(infinipath_init);
module_exit(infinipath_cleanup);
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_eeprom.c b/trunk/drivers/infiniband/hw/ipath/ipath_eeprom.c
index e7c25dbbcdc9..e28a42f53769 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_eeprom.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_eeprom.c
@@ -510,10 +510,10 @@ int ipath_eeprom_read(struct ipath_devdata *dd, u8 eeprom_offset,
{
int ret;
- ret = down_interruptible(&dd->ipath_eep_sem);
+ ret = mutex_lock_interruptible(&dd->ipath_eep_lock);
if (!ret) {
ret = ipath_eeprom_internal_read(dd, eeprom_offset, buff, len);
- up(&dd->ipath_eep_sem);
+ mutex_unlock(&dd->ipath_eep_lock);
}
return ret;
@@ -524,10 +524,10 @@ int ipath_eeprom_write(struct ipath_devdata *dd, u8 eeprom_offset,
{
int ret;
- ret = down_interruptible(&dd->ipath_eep_sem);
+ ret = mutex_lock_interruptible(&dd->ipath_eep_lock);
if (!ret) {
ret = ipath_eeprom_internal_write(dd, eeprom_offset, buff, len);
- up(&dd->ipath_eep_sem);
+ mutex_unlock(&dd->ipath_eep_lock);
}
return ret;
@@ -574,7 +574,7 @@ void ipath_get_eeprom_info(struct ipath_devdata *dd)
struct ipath_devdata *dd0 = ipath_lookup(0);
if (t && dd0->ipath_nguid > 1 && t <= dd0->ipath_nguid) {
- u8 *bguid, oguid;
+ u8 oguid;
dd->ipath_guid = dd0->ipath_guid;
bguid = (u8 *) & dd->ipath_guid;
@@ -616,9 +616,9 @@ void ipath_get_eeprom_info(struct ipath_devdata *dd)
goto bail;
}
- down(&dd->ipath_eep_sem);
+ mutex_lock(&dd->ipath_eep_lock);
eep_stat = ipath_eeprom_internal_read(dd, 0, buf, len);
- up(&dd->ipath_eep_sem);
+ mutex_unlock(&dd->ipath_eep_lock);
if (eep_stat) {
ipath_dev_err(dd, "Failed reading GUID from eeprom\n");
@@ -674,7 +674,6 @@ void ipath_get_eeprom_info(struct ipath_devdata *dd)
* elsewhere for backward-compatibility.
*/
char *snp = dd->ipath_serial;
- int len;
memcpy(snp, ifp->if_sprefix, sizeof ifp->if_sprefix);
snp[sizeof ifp->if_sprefix] = '\0';
len = strlen(snp);
@@ -764,14 +763,14 @@ int ipath_update_eeprom_log(struct ipath_devdata *dd)
/* Grab semaphore and read current EEPROM. If we get an
* error, let go, but if not, keep it until we finish write.
*/
- ret = down_interruptible(&dd->ipath_eep_sem);
+ ret = mutex_lock_interruptible(&dd->ipath_eep_lock);
if (ret) {
ipath_dev_err(dd, "Unable to acquire EEPROM for logging\n");
goto free_bail;
}
ret = ipath_eeprom_internal_read(dd, 0, buf, len);
if (ret) {
- up(&dd->ipath_eep_sem);
+ mutex_unlock(&dd->ipath_eep_lock);
ipath_dev_err(dd, "Unable read EEPROM for logging\n");
goto free_bail;
}
@@ -779,7 +778,7 @@ int ipath_update_eeprom_log(struct ipath_devdata *dd)
csum = flash_csum(ifp, 0);
if (csum != ifp->if_csum) {
- up(&dd->ipath_eep_sem);
+ mutex_unlock(&dd->ipath_eep_lock);
ipath_dev_err(dd, "EEPROM cks err (0x%02X, S/B 0x%02X)\n",
csum, ifp->if_csum);
ret = 1;
@@ -849,7 +848,7 @@ int ipath_update_eeprom_log(struct ipath_devdata *dd)
csum = flash_csum(ifp, 1);
ret = ipath_eeprom_internal_write(dd, 0, buf, hi_water + 1);
}
- up(&dd->ipath_eep_sem);
+ mutex_unlock(&dd->ipath_eep_lock);
if (ret)
ipath_dev_err(dd, "Failed updating EEPROM\n");
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_file_ops.c b/trunk/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 5de3243a47c3..7e025c8e01b6 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -169,7 +169,7 @@ static int ipath_get_base_info(struct file *fp,
kinfo->spi_piocnt = dd->ipath_pbufsport;
kinfo->spi_piobufbase = (u64) pd->port_piobufs;
kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
- dd->ipath_palign * pd->port_port;
+ dd->ipath_ureg_align * pd->port_port;
} else if (master) {
kinfo->spi_piocnt = (dd->ipath_pbufsport / subport_cnt) +
(dd->ipath_pbufsport % subport_cnt);
@@ -186,7 +186,7 @@ static int ipath_get_base_info(struct file *fp,
}
if (shared) {
kinfo->spi_port_uregbase = (u64) dd->ipath_uregbase +
- dd->ipath_palign * pd->port_port;
+ dd->ipath_ureg_align * pd->port_port;
kinfo->spi_port_rcvegrbuf = kinfo->spi_rcv_egrbufs;
kinfo->spi_port_rcvhdr_base = kinfo->spi_rcvhdr_base;
kinfo->spi_port_rcvhdr_tailaddr = kinfo->spi_rcvhdr_tailaddr;
@@ -742,11 +742,12 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, unsigned subport,
* updated and correct itself, even in the face of software
* bugs.
*/
- *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0;
- set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
+ if (pd->port_rcvhdrtail_kvaddr)
+ ipath_clear_rcvhdrtail(pd);
+ set_bit(dd->ipath_r_portenable_shift + pd->port_port,
&dd->ipath_rcvctrl);
} else
- clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
+ clear_bit(dd->ipath_r_portenable_shift + pd->port_port,
&dd->ipath_rcvctrl);
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
@@ -881,7 +882,7 @@ static int ipath_create_user_egr(struct ipath_portdata *pd)
egrcnt = dd->ipath_rcvegrcnt;
/* TID number offset for this port */
- egroff = pd->port_port * egrcnt;
+ egroff = (pd->port_port - 1) * egrcnt + dd->ipath_p0_rcvegrcnt;
egrsize = dd->ipath_rcvegrbufsize;
ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid "
"offset %x, egrsize %u\n", egrcnt, egroff, egrsize);
@@ -1049,11 +1050,6 @@ static int mmap_piobufs(struct vm_area_struct *vma,
phys = dd->ipath_physaddr + piobufs;
- /*
- * Don't mark this as non-cached, or we don't get the
- * write combining behavior we want on the PIO buffers!
- */
-
#if defined(__powerpc__)
/* There isn't a generic way to specify writethrough mappings */
pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
@@ -1120,33 +1116,24 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
}
/*
- * ipath_file_vma_nopage - handle a VMA page fault.
+ * ipath_file_vma_fault - handle a VMA page fault.
*/
-static struct page *ipath_file_vma_nopage(struct vm_area_struct *vma,
- unsigned long address, int *type)
+static int ipath_file_vma_fault(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
{
- unsigned long offset = address - vma->vm_start;
- struct page *page = NOPAGE_SIGBUS;
- void *pageptr;
+ struct page *page;
- /*
- * Convert the vmalloc address into a struct page.
- */
- pageptr = (void *)(offset + (vma->vm_pgoff << PAGE_SHIFT));
- page = vmalloc_to_page(pageptr);
+ page = vmalloc_to_page((void *)(vmf->pgoff << PAGE_SHIFT));
if (!page)
- goto out;
-
- /* Increment the reference count. */
+ return VM_FAULT_SIGBUS;
get_page(page);
- if (type)
- *type = VM_FAULT_MINOR;
-out:
- return page;
+ vmf->page = page;
+
+ return 0;
}
static struct vm_operations_struct ipath_file_vm_ops = {
- .nopage = ipath_file_vma_nopage,
+ .fault = ipath_file_vma_fault,
};
static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr,
@@ -1284,7 +1271,7 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
goto bail;
}
- ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+ ureg = dd->ipath_uregbase + dd->ipath_ureg_align * pd->port_port;
if (!pd->port_subport_cnt) {
/* port is not shared */
piocnt = dd->ipath_pbufsport;
@@ -1400,7 +1387,10 @@ static unsigned int ipath_poll_next(struct ipath_portdata *pd,
pollflag = ipath_poll_hdrqfull(pd);
head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port);
- tail = *(volatile u64 *)pd->port_rcvhdrtail_kvaddr;
+ if (pd->port_rcvhdrtail_kvaddr)
+ tail = ipath_get_rcvhdrtail(pd);
+ else
+ tail = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
if (head != tail)
pollflag |= POLLIN | POLLRDNORM;
@@ -1410,7 +1400,7 @@ static unsigned int ipath_poll_next(struct ipath_portdata *pd,
/* flush waiting flag so we don't miss an event */
wmb();
- set_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,
+ set_bit(pd->port_port + dd->ipath_r_intravail_shift,
&dd->ipath_rcvctrl);
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
@@ -1790,6 +1780,7 @@ static int find_shared_port(struct file *fp,
}
port_fp(fp) = pd;
subport_fp(fp) = pd->port_cnt++;
+ pd->port_subpid[subport_fp(fp)] = current->pid;
tidcursor_fp(fp) = 0;
pd->active_slaves |= 1 << subport_fp(fp);
ipath_cdbg(PROC,
@@ -1920,8 +1911,7 @@ static int ipath_do_user_init(struct file *fp,
*/
head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port);
ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port);
- dd->ipath_lastegrheads[pd->port_port] = -1;
- dd->ipath_lastrcvhdrqtails[pd->port_port] = -1;
+ pd->port_lastrcvhdrqtail = -1;
ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
pd->port_port, head32);
pd->port_tidcursor = 0; /* start at beginning after open */
@@ -1941,11 +1931,13 @@ static int ipath_do_user_init(struct file *fp,
* We explictly set the in-memory copy to 0 beforehand, so we don't
* have to wait to be sure the DMA update has happened.
*/
- *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0ULL;
- set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
+ if (pd->port_rcvhdrtail_kvaddr)
+ ipath_clear_rcvhdrtail(pd);
+ set_bit(dd->ipath_r_portenable_shift + pd->port_port,
&dd->ipath_rcvctrl);
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
- dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
+ dd->ipath_rcvctrl &
+ ~(1ULL << dd->ipath_r_tailupd_shift));
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
/* Notify any waiting slaves */
@@ -2022,6 +2014,7 @@ static int ipath_close(struct inode *in, struct file *fp)
* the slave(s) don't wait for receive data forever.
*/
pd->active_slaves &= ~(1 << fd->subport);
+ pd->port_subpid[fd->subport] = 0;
mutex_unlock(&ipath_mutex);
goto bail;
}
@@ -2054,9 +2047,9 @@ static int ipath_close(struct inode *in, struct file *fp)
if (dd->ipath_kregbase) {
int i;
/* atomically clear receive enable port and intr avail. */
- clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
+ clear_bit(dd->ipath_r_portenable_shift + port,
&dd->ipath_rcvctrl);
- clear_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,
+ clear_bit(pd->port_port + dd->ipath_r_intravail_shift,
&dd->ipath_rcvctrl);
ipath_write_kreg( dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
@@ -2149,11 +2142,15 @@ static int ipath_get_slave_info(struct ipath_portdata *pd,
static int ipath_force_pio_avail_update(struct ipath_devdata *dd)
{
- u64 reg = dd->ipath_sendctrl;
+ unsigned long flags;
- clear_bit(IPATH_S_PIOBUFAVAILUPD, ®);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, reg);
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
+ dd->ipath_sendctrl & ~INFINIPATH_S_PIOBUFAVAILUPD);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
return 0;
}
@@ -2227,6 +2224,11 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
dest = &cmd.cmd.poll_type;
src = &ucmd->cmd.poll_type;
break;
+ case IPATH_CMD_ARMLAUNCH_CTRL:
+ copy = sizeof(cmd.cmd.armlaunch_ctrl);
+ dest = &cmd.cmd.armlaunch_ctrl;
+ src = &ucmd->cmd.armlaunch_ctrl;
+ break;
default:
ret = -EINVAL;
goto bail;
@@ -2302,6 +2304,12 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
case IPATH_CMD_POLL_TYPE:
pd->poll_type = cmd.cmd.poll_type;
break;
+ case IPATH_CMD_ARMLAUNCH_CTRL:
+ if (cmd.cmd.armlaunch_ctrl)
+ ipath_enable_armlaunch(pd->port_dd);
+ else
+ ipath_disable_armlaunch(pd->port_dd);
+ break;
}
if (ret >= 0)
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_fs.c b/trunk/drivers/infiniband/hw/ipath/ipath_fs.c
index 262c25db05cd..23faba9d21eb 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_fs.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_fs.c
@@ -108,21 +108,16 @@ static const struct file_operations atomic_stats_ops = {
.read = atomic_stats_read,
};
-#define NUM_COUNTERS sizeof(struct infinipath_counters) / sizeof(u64)
-
static ssize_t atomic_counters_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- u64 counters[NUM_COUNTERS];
- u16 i;
+ struct infinipath_counters counters;
struct ipath_devdata *dd;
dd = file->f_path.dentry->d_inode->i_private;
+ dd->ipath_f_read_counters(dd, &counters);
- for (i = 0; i < NUM_COUNTERS; i++)
- counters[i] = ipath_snap_cntr(dd, i);
-
- return simple_read_from_buffer(buf, count, ppos, counters,
+ return simple_read_from_buffer(buf, count, ppos, &counters,
sizeof counters);
}
@@ -243,8 +238,7 @@ static int create_device_files(struct super_block *sb,
snprintf(unit, sizeof unit, "%02d", dd->ipath_unit);
ret = create_file(unit, S_IFDIR|S_IRUGO|S_IXUGO, sb->s_root, &dir,
- (struct file_operations *) &simple_dir_operations,
- dd);
+ &simple_dir_operations, dd);
if (ret) {
printk(KERN_ERR "create_file(%s) failed: %d\n", unit, ret);
goto bail;
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_iba6110.c b/trunk/drivers/infiniband/hw/ipath/ipath_iba6110.c
index ddbebe4bdb27..9e2ced3cdc5e 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -148,10 +148,57 @@ struct _infinipath_do_not_use_kernel_regs {
unsigned long long ReservedSW2[4];
};
-#define IPATH_KREG_OFFSET(field) (offsetof(struct \
- _infinipath_do_not_use_kernel_regs, field) / sizeof(u64))
+struct _infinipath_do_not_use_counters {
+ __u64 LBIntCnt;
+ __u64 LBFlowStallCnt;
+ __u64 Reserved1;
+ __u64 TxUnsupVLErrCnt;
+ __u64 TxDataPktCnt;
+ __u64 TxFlowPktCnt;
+ __u64 TxDwordCnt;
+ __u64 TxLenErrCnt;
+ __u64 TxMaxMinLenErrCnt;
+ __u64 TxUnderrunCnt;
+ __u64 TxFlowStallCnt;
+ __u64 TxDroppedPktCnt;
+ __u64 RxDroppedPktCnt;
+ __u64 RxDataPktCnt;
+ __u64 RxFlowPktCnt;
+ __u64 RxDwordCnt;
+ __u64 RxLenErrCnt;
+ __u64 RxMaxMinLenErrCnt;
+ __u64 RxICRCErrCnt;
+ __u64 RxVCRCErrCnt;
+ __u64 RxFlowCtrlErrCnt;
+ __u64 RxBadFormatCnt;
+ __u64 RxLinkProblemCnt;
+ __u64 RxEBPCnt;
+ __u64 RxLPCRCErrCnt;
+ __u64 RxBufOvflCnt;
+ __u64 RxTIDFullErrCnt;
+ __u64 RxTIDValidErrCnt;
+ __u64 RxPKeyMismatchCnt;
+ __u64 RxP0HdrEgrOvflCnt;
+ __u64 RxP1HdrEgrOvflCnt;
+ __u64 RxP2HdrEgrOvflCnt;
+ __u64 RxP3HdrEgrOvflCnt;
+ __u64 RxP4HdrEgrOvflCnt;
+ __u64 RxP5HdrEgrOvflCnt;
+ __u64 RxP6HdrEgrOvflCnt;
+ __u64 RxP7HdrEgrOvflCnt;
+ __u64 RxP8HdrEgrOvflCnt;
+ __u64 Reserved6;
+ __u64 Reserved7;
+ __u64 IBStatusChangeCnt;
+ __u64 IBLinkErrRecoveryCnt;
+ __u64 IBLinkDownedCnt;
+ __u64 IBSymbolErrCnt;
+};
+
+#define IPATH_KREG_OFFSET(field) (offsetof( \
+ struct _infinipath_do_not_use_kernel_regs, field) / sizeof(u64))
#define IPATH_CREG_OFFSET(field) (offsetof( \
- struct infinipath_counters, field) / sizeof(u64))
+ struct _infinipath_do_not_use_counters, field) / sizeof(u64))
static const struct ipath_kregs ipath_ht_kregs = {
.kr_control = IPATH_KREG_OFFSET(Control),
@@ -282,6 +329,9 @@ static const struct ipath_cregs ipath_ht_cregs = {
#define INFINIPATH_HWE_HTAPLL_RFSLIP 0x1000000000000000ULL
#define INFINIPATH_HWE_SERDESPLLFAILED 0x2000000000000000ULL
+#define IBA6110_IBCS_LINKTRAININGSTATE_MASK 0xf
+#define IBA6110_IBCS_LINKSTATE_SHIFT 4
+
/* kr_extstatus bits */
#define INFINIPATH_EXTS_FREQSEL 0x2
#define INFINIPATH_EXTS_SERDESSEL 0x4
@@ -296,6 +346,12 @@ static const struct ipath_cregs ipath_ht_cregs = {
#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFFULL
#define INFINIPATH_RT_BUFSIZE_SHIFT 48
+#define INFINIPATH_R_INTRAVAIL_SHIFT 16
+#define INFINIPATH_R_TAILUPD_SHIFT 31
+
+/* kr_xgxsconfig bits */
+#define INFINIPATH_XGXS_RESET 0x7ULL
+
/*
* masks and bits that are different in different chips, or present only
* in one
@@ -652,7 +708,6 @@ static int ipath_ht_boardname(struct ipath_devdata *dd, char *name,
"with ID %u\n", boardrev);
snprintf(name, namelen, "Unknown_InfiniPath_QHT7xxx_%u",
boardrev);
- ret = 1;
break;
}
if (n)
@@ -686,6 +741,13 @@ static int ipath_ht_boardname(struct ipath_devdata *dd, char *name,
dd->ipath_htspeed);
ret = 0;
+ /*
+ * set here, not in ipath_init_*_funcs because we have to do
+ * it after we can read chip registers.
+ */
+ dd->ipath_ureg_align =
+ ipath_read_kreg32(dd, dd->ipath_kregs->kr_pagealign);
+
bail:
return ret;
}
@@ -969,7 +1031,8 @@ static int ipath_setup_ht_config(struct ipath_devdata *dd,
do {
u8 cap_type;
- /* the HT capability type byte is 3 bytes after the
+ /*
+ * The HT capability type byte is 3 bytes after the
* capability byte.
*/
if (pci_read_config_byte(pdev, pos + 3, &cap_type)) {
@@ -982,6 +1045,8 @@ static int ipath_setup_ht_config(struct ipath_devdata *dd,
} while ((pos = pci_find_next_capability(pdev, pos,
PCI_CAP_ID_HT)));
+ dd->ipath_flags |= IPATH_SWAP_PIOBUFS;
+
bail:
return ret;
}
@@ -1074,11 +1139,55 @@ static void ipath_setup_ht_setextled(struct ipath_devdata *dd,
static void ipath_init_ht_variables(struct ipath_devdata *dd)
{
+ /*
+ * setup the register offsets, since they are different for each
+ * chip
+ */
+ dd->ipath_kregs = &ipath_ht_kregs;
+ dd->ipath_cregs = &ipath_ht_cregs;
+
dd->ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
dd->ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
dd->ipath_gpio_sda = IPATH_GPIO_SDA;
dd->ipath_gpio_scl = IPATH_GPIO_SCL;
+ /*
+ * Fill in data for field-values that change in newer chips.
+ * We dynamically specify only the mask for LINKTRAININGSTATE
+ * and only the shift for LINKSTATE, as they are the only ones
+ * that change. Also precalculate the 3 link states of interest
+ * and the combined mask.
+ */
+ dd->ibcs_ls_shift = IBA6110_IBCS_LINKSTATE_SHIFT;
+ dd->ibcs_lts_mask = IBA6110_IBCS_LINKTRAININGSTATE_MASK;
+ dd->ibcs_mask = (INFINIPATH_IBCS_LINKSTATE_MASK <<
+ dd->ibcs_ls_shift) | dd->ibcs_lts_mask;
+ dd->ib_init = (INFINIPATH_IBCS_LT_STATE_LINKUP <<
+ INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) |
+ (INFINIPATH_IBCS_L_STATE_INIT << dd->ibcs_ls_shift);
+ dd->ib_arm = (INFINIPATH_IBCS_LT_STATE_LINKUP <<
+ INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) |
+ (INFINIPATH_IBCS_L_STATE_ARM << dd->ibcs_ls_shift);
+ dd->ib_active = (INFINIPATH_IBCS_LT_STATE_LINKUP <<
+ INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) |
+ (INFINIPATH_IBCS_L_STATE_ACTIVE << dd->ibcs_ls_shift);
+
+ /*
+ * Fill in data for ibcc field-values that change in newer chips.
+ * We dynamically specify only the mask for LINKINITCMD
+ * and only the shift for LINKCMD and MAXPKTLEN, as they are
+ * the only ones that change.
+ */
+ dd->ibcc_lic_mask = INFINIPATH_IBCC_LINKINITCMD_MASK;
+ dd->ibcc_lc_shift = INFINIPATH_IBCC_LINKCMD_SHIFT;
+ dd->ibcc_mpl_shift = INFINIPATH_IBCC_MAXPKTLEN_SHIFT;
+
+ /* Fill in shifts for RcvCtrl. */
+ dd->ipath_r_portenable_shift = INFINIPATH_R_PORTENABLE_SHIFT;
+ dd->ipath_r_intravail_shift = INFINIPATH_R_INTRAVAIL_SHIFT;
+ dd->ipath_r_tailupd_shift = INFINIPATH_R_TAILUPD_SHIFT;
+ dd->ipath_r_portcfg_shift = 0; /* Not on IBA6110 */
+
dd->ipath_i_bitsextant =
(INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) |
(INFINIPATH_I_RCVAVAIL_MASK <<
@@ -1135,6 +1244,8 @@ static void ipath_init_ht_variables(struct ipath_devdata *dd)
dd->ipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
dd->ipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
+ dd->ipath_i_rcvavail_shift = INFINIPATH_I_RCVAVAIL_SHIFT;
+ dd->ipath_i_rcvurg_shift = INFINIPATH_I_RCVURG_SHIFT;
/*
* EEPROM error log 0 is TXE Parity errors. 1 is RXE Parity.
@@ -1148,9 +1259,17 @@ static void ipath_init_ht_variables(struct ipath_devdata *dd)
INFINIPATH_HWE_RXEMEMPARITYERR_MASK <<
INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT;
- dd->ipath_eep_st_masks[2].errs_to_log =
- INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET;
+ dd->ipath_eep_st_masks[2].errs_to_log = INFINIPATH_E_RESET;
+ dd->delay_mult = 2; /* SDR, 4X, can't change */
+
+ dd->ipath_link_width_supported = IB_WIDTH_1X | IB_WIDTH_4X;
+ dd->ipath_link_speed_supported = IPATH_IB_SDR;
+ dd->ipath_link_width_enabled = IB_WIDTH_4X;
+ dd->ipath_link_speed_enabled = dd->ipath_link_speed_supported;
+ /* these can't change for this chip, so set once */
+ dd->ipath_link_width_active = dd->ipath_link_width_enabled;
+ dd->ipath_link_speed_active = dd->ipath_link_speed_enabled;
}
/**
@@ -1205,14 +1324,16 @@ static void ipath_ht_init_hwerrors(struct ipath_devdata *dd)
val &= ~INFINIPATH_HWE_HTCMISCERR4;
/*
- * PLL ignored because MDIO interface has a logic problem
- * for reads, on Comstock and Ponderosa. BRINGUP
+ * PLL ignored because unused MDIO interface has a logic problem
*/
if (dd->ipath_boardrev == 4 || dd->ipath_boardrev == 9)
val &= ~INFINIPATH_HWE_SERDESPLLFAILED;
dd->ipath_hwerrmask = val;
}
+
+
+
/**
* ipath_ht_bringup_serdes - bring up the serdes
* @dd: the infinipath device
@@ -1284,16 +1405,6 @@ static int ipath_ht_bringup_serdes(struct ipath_devdata *dd)
}
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig);
- if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) &
- INFINIPATH_XGXS_MDIOADDR_MASK) != 3) {
- val &= ~(INFINIPATH_XGXS_MDIOADDR_MASK <<
- INFINIPATH_XGXS_MDIOADDR_SHIFT);
- /*
- * we use address 3
- */
- val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT;
- change = 1;
- }
if (val & INFINIPATH_XGXS_RESET) {
/* normally true after boot */
val &= ~INFINIPATH_XGXS_RESET;
@@ -1329,21 +1440,6 @@ static int ipath_ht_bringup_serdes(struct ipath_devdata *dd)
(unsigned long long)
ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig));
- if (!ipath_waitfor_mdio_cmdready(dd)) {
- ipath_write_kreg(dd, dd->ipath_kregs->kr_mdio,
- ipath_mdio_req(IPATH_MDIO_CMD_READ, 31,
- IPATH_MDIO_CTRL_XGXS_REG_8,
- 0));
- if (ipath_waitfor_complete(dd, dd->ipath_kregs->kr_mdio,
- IPATH_MDIO_DATAVALID, &val))
- ipath_dbg("Never got MDIO data for XGXS status "
- "read\n");
- else
- ipath_cdbg(VERBOSE, "MDIO Read reg8, "
- "'bank' 31 %x\n", (u32) val);
- } else
- ipath_dbg("Never got MDIO cmdready for XGXS status read\n");
-
return ret; /* for now, say we always succeeded */
}
@@ -1396,6 +1492,7 @@ static void ipath_ht_put_tid(struct ipath_devdata *dd,
pa |= lenvalid | INFINIPATH_RT_VALID;
}
}
+
writeq(pa, tidptr);
}
@@ -1526,8 +1623,7 @@ static int ipath_ht_early_init(struct ipath_devdata *dd)
}
ipath_get_eeprom_info(dd);
- if (dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' &&
- dd->ipath_serial[1] == '2' && dd->ipath_serial[2] == '8') {
+ if (dd->ipath_boardrev == 5) {
/*
* Later production QHT7040 has same changes as QHT7140, so
* can use GPIO interrupts. They have serial #'s starting
@@ -1602,6 +1698,210 @@ static void ipath_ht_free_irq(struct ipath_devdata *dd)
dd->ipath_intconfig = 0;
}
+static struct ipath_message_header *
+ipath_ht_get_msgheader(struct ipath_devdata *dd, __le32 *rhf_addr)
+{
+ return (struct ipath_message_header *)
+ &rhf_addr[sizeof(u64) / sizeof(u32)];
+}
+
+static void ipath_ht_config_ports(struct ipath_devdata *dd, ushort cfgports)
+{
+ dd->ipath_portcnt =
+ ipath_read_kreg32(dd, dd->ipath_kregs->kr_portcnt);
+ dd->ipath_p0_rcvegrcnt =
+ ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrcnt);
+}
+
+static void ipath_ht_read_counters(struct ipath_devdata *dd,
+ struct infinipath_counters *cntrs)
+{
+ cntrs->LBIntCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(LBIntCnt));
+ cntrs->LBFlowStallCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(LBFlowStallCnt));
+ cntrs->TxSDmaDescCnt = 0;
+ cntrs->TxUnsupVLErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxUnsupVLErrCnt));
+ cntrs->TxDataPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxDataPktCnt));
+ cntrs->TxFlowPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxFlowPktCnt));
+ cntrs->TxDwordCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxDwordCnt));
+ cntrs->TxLenErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxLenErrCnt));
+ cntrs->TxMaxMinLenErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxMaxMinLenErrCnt));
+ cntrs->TxUnderrunCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxUnderrunCnt));
+ cntrs->TxFlowStallCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxFlowStallCnt));
+ cntrs->TxDroppedPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxDroppedPktCnt));
+ cntrs->RxDroppedPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxDroppedPktCnt));
+ cntrs->RxDataPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxDataPktCnt));
+ cntrs->RxFlowPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxFlowPktCnt));
+ cntrs->RxDwordCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxDwordCnt));
+ cntrs->RxLenErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxLenErrCnt));
+ cntrs->RxMaxMinLenErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxMaxMinLenErrCnt));
+ cntrs->RxICRCErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxICRCErrCnt));
+ cntrs->RxVCRCErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxVCRCErrCnt));
+ cntrs->RxFlowCtrlErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxFlowCtrlErrCnt));
+ cntrs->RxBadFormatCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxBadFormatCnt));
+ cntrs->RxLinkProblemCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxLinkProblemCnt));
+ cntrs->RxEBPCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxEBPCnt));
+ cntrs->RxLPCRCErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxLPCRCErrCnt));
+ cntrs->RxBufOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxBufOvflCnt));
+ cntrs->RxTIDFullErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxTIDFullErrCnt));
+ cntrs->RxTIDValidErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxTIDValidErrCnt));
+ cntrs->RxPKeyMismatchCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxPKeyMismatchCnt));
+ cntrs->RxP0HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP0HdrEgrOvflCnt));
+ cntrs->RxP1HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP1HdrEgrOvflCnt));
+ cntrs->RxP2HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP2HdrEgrOvflCnt));
+ cntrs->RxP3HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP3HdrEgrOvflCnt));
+ cntrs->RxP4HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP4HdrEgrOvflCnt));
+ cntrs->RxP5HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP5HdrEgrOvflCnt));
+ cntrs->RxP6HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP6HdrEgrOvflCnt));
+ cntrs->RxP7HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP7HdrEgrOvflCnt));
+ cntrs->RxP8HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP8HdrEgrOvflCnt));
+ cntrs->RxP9HdrEgrOvflCnt = 0;
+ cntrs->RxP10HdrEgrOvflCnt = 0;
+ cntrs->RxP11HdrEgrOvflCnt = 0;
+ cntrs->RxP12HdrEgrOvflCnt = 0;
+ cntrs->RxP13HdrEgrOvflCnt = 0;
+ cntrs->RxP14HdrEgrOvflCnt = 0;
+ cntrs->RxP15HdrEgrOvflCnt = 0;
+ cntrs->RxP16HdrEgrOvflCnt = 0;
+ cntrs->IBStatusChangeCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(IBStatusChangeCnt));
+ cntrs->IBLinkErrRecoveryCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(IBLinkErrRecoveryCnt));
+ cntrs->IBLinkDownedCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(IBLinkDownedCnt));
+ cntrs->IBSymbolErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(IBSymbolErrCnt));
+ cntrs->RxVL15DroppedPktCnt = 0;
+ cntrs->RxOtherLocalPhyErrCnt = 0;
+ cntrs->PcieRetryBufDiagQwordCnt = 0;
+ cntrs->ExcessBufferOvflCnt = dd->ipath_overrun_thresh_errs;
+ cntrs->LocalLinkIntegrityErrCnt =
+ (dd->ipath_flags & IPATH_GPIO_ERRINTRS) ?
+ dd->ipath_lli_errs : dd->ipath_lli_errors;
+ cntrs->RxVlErrCnt = 0;
+ cntrs->RxDlidFltrCnt = 0;
+}
+
+
+/* no interrupt fallback for these chips */
+static int ipath_ht_nointr_fallback(struct ipath_devdata *dd)
+{
+ return 0;
+}
+
+
+/*
+ * reset the XGXS (between serdes and IBC). Slightly less intrusive
+ * than resetting the IBC or external link state, and useful in some
+ * cases to cause some retraining. To do this right, we reset IBC
+ * as well.
+ */
+static void ipath_ht_xgxs_reset(struct ipath_devdata *dd)
+{
+ u64 val, prev_val;
+
+ prev_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig);
+ val = prev_val | INFINIPATH_XGXS_RESET;
+ prev_val &= ~INFINIPATH_XGXS_RESET; /* be sure */
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+ dd->ipath_control & ~INFINIPATH_C_LINKENABLE);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val);
+ ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, prev_val);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+ dd->ipath_control);
+}
+
+
+static int ipath_ht_get_ib_cfg(struct ipath_devdata *dd, int which)
+{
+ int ret;
+
+ switch (which) {
+ case IPATH_IB_CFG_LWID:
+ ret = dd->ipath_link_width_active;
+ break;
+ case IPATH_IB_CFG_SPD:
+ ret = dd->ipath_link_speed_active;
+ break;
+ case IPATH_IB_CFG_LWID_ENB:
+ ret = dd->ipath_link_width_enabled;
+ break;
+ case IPATH_IB_CFG_SPD_ENB:
+ ret = dd->ipath_link_speed_enabled;
+ break;
+ default:
+ ret = -ENOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+
+/* we assume range checking is already done, if needed */
+static int ipath_ht_set_ib_cfg(struct ipath_devdata *dd, int which, u32 val)
+{
+ int ret = 0;
+
+ if (which == IPATH_IB_CFG_LWID_ENB)
+ dd->ipath_link_width_enabled = val;
+ else if (which == IPATH_IB_CFG_SPD_ENB)
+ dd->ipath_link_speed_enabled = val;
+ else
+ ret = -ENOTSUPP;
+ return ret;
+}
+
+
+static void ipath_ht_config_jint(struct ipath_devdata *dd, u16 a, u16 b)
+{
+}
+
+
+static int ipath_ht_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
+{
+ ipath_setup_ht_setextled(dd, ipath_ib_linkstate(dd, ibcs),
+ ipath_ib_linktrstate(dd, ibcs));
+ return 0;
+}
+
+
/**
* ipath_init_iba6110_funcs - set up the chip-specific function pointers
* @dd: the infinipath device
@@ -1626,22 +1926,19 @@ void ipath_init_iba6110_funcs(struct ipath_devdata *dd)
dd->ipath_f_setextled = ipath_setup_ht_setextled;
dd->ipath_f_get_base_info = ipath_ht_get_base_info;
dd->ipath_f_free_irq = ipath_ht_free_irq;
-
- /*
- * initialize chip-specific variables
- */
dd->ipath_f_tidtemplate = ipath_ht_tidtemplate;
+ dd->ipath_f_intr_fallback = ipath_ht_nointr_fallback;
+ dd->ipath_f_get_msgheader = ipath_ht_get_msgheader;
+ dd->ipath_f_config_ports = ipath_ht_config_ports;
+ dd->ipath_f_read_counters = ipath_ht_read_counters;
+ dd->ipath_f_xgxs_reset = ipath_ht_xgxs_reset;
+ dd->ipath_f_get_ib_cfg = ipath_ht_get_ib_cfg;
+ dd->ipath_f_set_ib_cfg = ipath_ht_set_ib_cfg;
+ dd->ipath_f_config_jint = ipath_ht_config_jint;
+ dd->ipath_f_ib_updown = ipath_ht_ib_updown;
/*
- * setup the register offsets, since they are different for each
- * chip
- */
- dd->ipath_kregs = &ipath_ht_kregs;
- dd->ipath_cregs = &ipath_ht_cregs;
-
- /*
- * do very early init that is needed before ipath_f_bus is
- * called
+ * initialize chip-specific variables
*/
ipath_init_ht_variables(dd);
}
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_iba6120.c b/trunk/drivers/infiniband/hw/ipath/ipath_iba6120.c
index 0103d6f4847b..c7a2f50824c0 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_iba6120.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_iba6120.c
@@ -145,10 +145,57 @@ struct _infinipath_do_not_use_kernel_regs {
unsigned long long Reserved12;
};
-#define IPATH_KREG_OFFSET(field) (offsetof(struct \
- _infinipath_do_not_use_kernel_regs, field) / sizeof(u64))
+struct _infinipath_do_not_use_counters {
+ __u64 LBIntCnt;
+ __u64 LBFlowStallCnt;
+ __u64 Reserved1;
+ __u64 TxUnsupVLErrCnt;
+ __u64 TxDataPktCnt;
+ __u64 TxFlowPktCnt;
+ __u64 TxDwordCnt;
+ __u64 TxLenErrCnt;
+ __u64 TxMaxMinLenErrCnt;
+ __u64 TxUnderrunCnt;
+ __u64 TxFlowStallCnt;
+ __u64 TxDroppedPktCnt;
+ __u64 RxDroppedPktCnt;
+ __u64 RxDataPktCnt;
+ __u64 RxFlowPktCnt;
+ __u64 RxDwordCnt;
+ __u64 RxLenErrCnt;
+ __u64 RxMaxMinLenErrCnt;
+ __u64 RxICRCErrCnt;
+ __u64 RxVCRCErrCnt;
+ __u64 RxFlowCtrlErrCnt;
+ __u64 RxBadFormatCnt;
+ __u64 RxLinkProblemCnt;
+ __u64 RxEBPCnt;
+ __u64 RxLPCRCErrCnt;
+ __u64 RxBufOvflCnt;
+ __u64 RxTIDFullErrCnt;
+ __u64 RxTIDValidErrCnt;
+ __u64 RxPKeyMismatchCnt;
+ __u64 RxP0HdrEgrOvflCnt;
+ __u64 RxP1HdrEgrOvflCnt;
+ __u64 RxP2HdrEgrOvflCnt;
+ __u64 RxP3HdrEgrOvflCnt;
+ __u64 RxP4HdrEgrOvflCnt;
+ __u64 RxP5HdrEgrOvflCnt;
+ __u64 RxP6HdrEgrOvflCnt;
+ __u64 RxP7HdrEgrOvflCnt;
+ __u64 RxP8HdrEgrOvflCnt;
+ __u64 Reserved6;
+ __u64 Reserved7;
+ __u64 IBStatusChangeCnt;
+ __u64 IBLinkErrRecoveryCnt;
+ __u64 IBLinkDownedCnt;
+ __u64 IBSymbolErrCnt;
+};
+
+#define IPATH_KREG_OFFSET(field) (offsetof( \
+ struct _infinipath_do_not_use_kernel_regs, field) / sizeof(u64))
#define IPATH_CREG_OFFSET(field) (offsetof( \
- struct infinipath_counters, field) / sizeof(u64))
+ struct _infinipath_do_not_use_counters, field) / sizeof(u64))
static const struct ipath_kregs ipath_pe_kregs = {
.kr_control = IPATH_KREG_OFFSET(Control),
@@ -282,6 +329,9 @@ static const struct ipath_cregs ipath_pe_cregs = {
#define INFINIPATH_HWE_PCIE0PLLFAILED 0x0800000000000000ULL
#define INFINIPATH_HWE_SERDESPLLFAILED 0x1000000000000000ULL
+#define IBA6120_IBCS_LINKTRAININGSTATE_MASK 0xf
+#define IBA6120_IBCS_LINKSTATE_SHIFT 4
+
/* kr_extstatus bits */
#define INFINIPATH_EXTS_FREQSEL 0x2
#define INFINIPATH_EXTS_SERDESSEL 0x4
@@ -296,6 +346,9 @@ static const struct ipath_cregs ipath_pe_cregs = {
#define IPATH_GPIO_SCL (1ULL << \
(_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
+#define INFINIPATH_R_INTRAVAIL_SHIFT 16
+#define INFINIPATH_R_TAILUPD_SHIFT 31
+
/* 6120 specific hardware errors... */
static const struct ipath_hwerror_msgs ipath_6120_hwerror_msgs[] = {
INFINIPATH_HWE_MSG(PCIEPOISONEDTLP, "PCIe Poisoned TLP"),
@@ -320,10 +373,28 @@ static const struct ipath_hwerror_msgs ipath_6120_hwerror_msgs[] = {
INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \
<< INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)
-static int ipath_pe_txe_recover(struct ipath_devdata *);
static void ipath_pe_put_tid_2(struct ipath_devdata *, u64 __iomem *,
u32, unsigned long);
+/*
+ * On platforms using this chip, and not having ordered WC stores, we
+ * can get TXE parity errors due to speculative reads to the PIO buffers,
+ * and this, due to a chip bug can result in (many) false parity error
+ * reports. So it's a debug print on those, and an info print on systems
+ * where the speculative reads don't occur.
+ */
+static void ipath_pe_txe_recover(struct ipath_devdata *dd)
+{
+ if (ipath_unordered_wc())
+ ipath_dbg("Recovering from TXE PIO parity error\n");
+ else {
+ ++ipath_stats.sps_txeparity;
+ dev_info(&dd->pcidev->dev,
+ "Recovering from TXE PIO parity error\n");
+ }
+}
+
+
/**
* ipath_pe_handle_hwerrors - display hardware errors.
* @dd: the infinipath device
@@ -403,35 +474,11 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
* occur if a processor speculative read is done to the PIO
* buffer while we are sending a packet, for example.
*/
- if ((hwerrs & TXE_PIO_PARITY) && ipath_pe_txe_recover(dd))
+ if (hwerrs & TXE_PIO_PARITY) {
+ ipath_pe_txe_recover(dd);
hwerrs &= ~TXE_PIO_PARITY;
- if (hwerrs) {
- /*
- * if any set that we aren't ignoring only make the
- * complaint once, in case it's stuck or recurring,
- * and we get here multiple times
- * Force link down, so switch knows, and
- * LEDs are turned off
- */
- if (dd->ipath_flags & IPATH_INITTED) {
- ipath_set_linkstate(dd, IPATH_IB_LINKDOWN);
- ipath_setup_pe_setextled(dd,
- INFINIPATH_IBCS_L_STATE_DOWN,
- INFINIPATH_IBCS_LT_STATE_DISABLED);
- ipath_dev_err(dd, "Fatal Hardware Error (freeze "
- "mode), no longer usable, SN %.16s\n",
- dd->ipath_serial);
- isfatal = 1;
- }
- /*
- * Mark as having had an error for driver, and also
- * for /sys and status word mapped to user programs.
- * This marks unit as not usable, until reset
- */
- *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
- *dd->ipath_statusp |= IPATH_STATUS_HWERROR;
- dd->ipath_flags &= ~IPATH_INITTED;
- } else {
+ }
+ if (!hwerrs) {
static u32 freeze_cnt;
freeze_cnt++;
@@ -485,7 +532,7 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) {
/*
- * If it occurs, it is left masked since the eternal
+ * If it occurs, it is left masked since the external
* interface is unused
*/
dd->ipath_hwerrmask &= ~INFINIPATH_HWE_SERDESPLLFAILED;
@@ -563,6 +610,14 @@ static int ipath_pe_boardname(struct ipath_devdata *dd, char *name,
dd->ipath_f_put_tid = ipath_pe_put_tid_2;
}
+
+ /*
+ * set here, not in ipath_init_*_funcs because we have to do
+ * it after we can read chip registers.
+ */
+ dd->ipath_ureg_align =
+ ipath_read_kreg32(dd, dd->ipath_kregs->kr_pagealign);
+
return ret;
}
@@ -667,17 +722,8 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig);
prev_val = val;
- if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) &
- INFINIPATH_XGXS_MDIOADDR_MASK) != 3) {
- val &=
- ~(INFINIPATH_XGXS_MDIOADDR_MASK <<
- INFINIPATH_XGXS_MDIOADDR_SHIFT);
- /* MDIO address 3 */
- val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT;
- }
- if (val & INFINIPATH_XGXS_RESET) {
+ if (val & INFINIPATH_XGXS_RESET)
val &= ~INFINIPATH_XGXS_RESET;
- }
if (((val >> INFINIPATH_XGXS_RX_POL_SHIFT) &
INFINIPATH_XGXS_RX_POL_MASK) != dd->ipath_rx_pol_inv ) {
/* need to compensate for Tx inversion in partner */
@@ -707,21 +753,6 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
(unsigned long long)
ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig));
- if (!ipath_waitfor_mdio_cmdready(dd)) {
- ipath_write_kreg(
- dd, dd->ipath_kregs->kr_mdio,
- ipath_mdio_req(IPATH_MDIO_CMD_READ, 31,
- IPATH_MDIO_CTRL_XGXS_REG_8, 0));
- if (ipath_waitfor_complete(dd, dd->ipath_kregs->kr_mdio,
- IPATH_MDIO_DATAVALID, &val))
- ipath_dbg("Never got MDIO data for XGXS "
- "status read\n");
- else
- ipath_cdbg(VERBOSE, "MDIO Read reg8, "
- "'bank' 31 %x\n", (u32) val);
- } else
- ipath_dbg("Never got MDIO cmdready for XGXS status read\n");
-
return ret;
}
@@ -902,11 +933,26 @@ static int ipath_setup_pe_config(struct ipath_devdata *dd,
else
ipath_dev_err(dd, "Can't find PCI Express "
"capability!\n");
+
+ dd->ipath_link_width_supported = IB_WIDTH_1X | IB_WIDTH_4X;
+ dd->ipath_link_speed_supported = IPATH_IB_SDR;
+ dd->ipath_link_width_enabled = IB_WIDTH_4X;
+ dd->ipath_link_speed_enabled = dd->ipath_link_speed_supported;
+ /* these can't change for this chip, so set once */
+ dd->ipath_link_width_active = dd->ipath_link_width_enabled;
+ dd->ipath_link_speed_active = dd->ipath_link_speed_enabled;
return 0;
}
static void ipath_init_pe_variables(struct ipath_devdata *dd)
{
+ /*
+ * setup the register offsets, since they are different for each
+ * chip
+ */
+ dd->ipath_kregs = &ipath_pe_kregs;
+ dd->ipath_cregs = &ipath_pe_cregs;
+
/*
* bits for selecting i2c direction and values,
* used for I2C serial flash
@@ -916,6 +962,43 @@ static void ipath_init_pe_variables(struct ipath_devdata *dd)
dd->ipath_gpio_sda = IPATH_GPIO_SDA;
dd->ipath_gpio_scl = IPATH_GPIO_SCL;
+ /*
+ * Fill in data for field-values that change in newer chips.
+ * We dynamically specify only the mask for LINKTRAININGSTATE
+ * and only the shift for LINKSTATE, as they are the only ones
+ * that change. Also precalculate the 3 link states of interest
+ * and the combined mask.
+ */
+ dd->ibcs_ls_shift = IBA6120_IBCS_LINKSTATE_SHIFT;
+ dd->ibcs_lts_mask = IBA6120_IBCS_LINKTRAININGSTATE_MASK;
+ dd->ibcs_mask = (INFINIPATH_IBCS_LINKSTATE_MASK <<
+ dd->ibcs_ls_shift) | dd->ibcs_lts_mask;
+ dd->ib_init = (INFINIPATH_IBCS_LT_STATE_LINKUP <<
+ INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) |
+ (INFINIPATH_IBCS_L_STATE_INIT << dd->ibcs_ls_shift);
+ dd->ib_arm = (INFINIPATH_IBCS_LT_STATE_LINKUP <<
+ INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) |
+ (INFINIPATH_IBCS_L_STATE_ARM << dd->ibcs_ls_shift);
+ dd->ib_active = (INFINIPATH_IBCS_LT_STATE_LINKUP <<
+ INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) |
+ (INFINIPATH_IBCS_L_STATE_ACTIVE << dd->ibcs_ls_shift);
+
+ /*
+ * Fill in data for ibcc field-values that change in newer chips.
+ * We dynamically specify only the mask for LINKINITCMD
+ * and only the shift for LINKCMD and MAXPKTLEN, as they are
+ * the only ones that change.
+ */
+ dd->ibcc_lic_mask = INFINIPATH_IBCC_LINKINITCMD_MASK;
+ dd->ibcc_lc_shift = INFINIPATH_IBCC_LINKCMD_SHIFT;
+ dd->ibcc_mpl_shift = INFINIPATH_IBCC_MAXPKTLEN_SHIFT;
+
+ /* Fill in shifts for RcvCtrl. */
+ dd->ipath_r_portenable_shift = INFINIPATH_R_PORTENABLE_SHIFT;
+ dd->ipath_r_intravail_shift = INFINIPATH_R_INTRAVAIL_SHIFT;
+ dd->ipath_r_tailupd_shift = INFINIPATH_R_TAILUPD_SHIFT;
+ dd->ipath_r_portcfg_shift = 0; /* Not on IBA6120 */
+
/* variables for sanity checking interrupt and errors */
dd->ipath_hwe_bitsextant =
(INFINIPATH_HWE_RXEMEMPARITYERR_MASK <<
@@ -963,6 +1046,8 @@ static void ipath_init_pe_variables(struct ipath_devdata *dd)
dd->ipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
dd->ipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
+ dd->ipath_i_rcvavail_shift = INFINIPATH_I_RCVAVAIL_SHIFT;
+ dd->ipath_i_rcvurg_shift = INFINIPATH_I_RCVURG_SHIFT;
/*
* EEPROM error log 0 is TXE Parity errors. 1 is RXE Parity.
@@ -984,6 +1069,7 @@ static void ipath_init_pe_variables(struct ipath_devdata *dd)
INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET;
+ dd->delay_mult = 2; /* SDR, 4X, can't change */
}
/* setup the MSI stuff again after a reset. I'd like to just call
@@ -1289,6 +1375,9 @@ static int ipath_pe_early_init(struct ipath_devdata *dd)
*/
dd->ipath_rcvhdrentsize = 24;
dd->ipath_rcvhdrsize = IPATH_DFLT_RCVHDRSIZE;
+ dd->ipath_rhf_offset = 0;
+ dd->ipath_egrtidbase = (u64 __iomem *)
+ ((char __iomem *) dd->ipath_kregbase + dd->ipath_rcvegrbase);
/*
* To truly support a 4KB MTU (for usermode), we need to
@@ -1359,34 +1448,204 @@ static void ipath_pe_free_irq(struct ipath_devdata *dd)
dd->ipath_irq = 0;
}
+
+static struct ipath_message_header *
+ipath_pe_get_msgheader(struct ipath_devdata *dd, __le32 *rhf_addr)
+{
+ return (struct ipath_message_header *)
+ &rhf_addr[sizeof(u64) / sizeof(u32)];
+}
+
+static void ipath_pe_config_ports(struct ipath_devdata *dd, ushort cfgports)
+{
+ dd->ipath_portcnt =
+ ipath_read_kreg32(dd, dd->ipath_kregs->kr_portcnt);
+ dd->ipath_p0_rcvegrcnt =
+ ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrcnt);
+}
+
+static void ipath_pe_read_counters(struct ipath_devdata *dd,
+ struct infinipath_counters *cntrs)
+{
+ cntrs->LBIntCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(LBIntCnt));
+ cntrs->LBFlowStallCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(LBFlowStallCnt));
+ cntrs->TxSDmaDescCnt = 0;
+ cntrs->TxUnsupVLErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxUnsupVLErrCnt));
+ cntrs->TxDataPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxDataPktCnt));
+ cntrs->TxFlowPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxFlowPktCnt));
+ cntrs->TxDwordCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxDwordCnt));
+ cntrs->TxLenErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxLenErrCnt));
+ cntrs->TxMaxMinLenErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxMaxMinLenErrCnt));
+ cntrs->TxUnderrunCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxUnderrunCnt));
+ cntrs->TxFlowStallCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxFlowStallCnt));
+ cntrs->TxDroppedPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(TxDroppedPktCnt));
+ cntrs->RxDroppedPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxDroppedPktCnt));
+ cntrs->RxDataPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxDataPktCnt));
+ cntrs->RxFlowPktCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxFlowPktCnt));
+ cntrs->RxDwordCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxDwordCnt));
+ cntrs->RxLenErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxLenErrCnt));
+ cntrs->RxMaxMinLenErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxMaxMinLenErrCnt));
+ cntrs->RxICRCErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxICRCErrCnt));
+ cntrs->RxVCRCErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxVCRCErrCnt));
+ cntrs->RxFlowCtrlErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxFlowCtrlErrCnt));
+ cntrs->RxBadFormatCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxBadFormatCnt));
+ cntrs->RxLinkProblemCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxLinkProblemCnt));
+ cntrs->RxEBPCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxEBPCnt));
+ cntrs->RxLPCRCErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxLPCRCErrCnt));
+ cntrs->RxBufOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxBufOvflCnt));
+ cntrs->RxTIDFullErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxTIDFullErrCnt));
+ cntrs->RxTIDValidErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxTIDValidErrCnt));
+ cntrs->RxPKeyMismatchCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxPKeyMismatchCnt));
+ cntrs->RxP0HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP0HdrEgrOvflCnt));
+ cntrs->RxP1HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP1HdrEgrOvflCnt));
+ cntrs->RxP2HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP2HdrEgrOvflCnt));
+ cntrs->RxP3HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP3HdrEgrOvflCnt));
+ cntrs->RxP4HdrEgrOvflCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(RxP4HdrEgrOvflCnt));
+ cntrs->RxP5HdrEgrOvflCnt = 0;
+ cntrs->RxP6HdrEgrOvflCnt = 0;
+ cntrs->RxP7HdrEgrOvflCnt = 0;
+ cntrs->RxP8HdrEgrOvflCnt = 0;
+ cntrs->RxP9HdrEgrOvflCnt = 0;
+ cntrs->RxP10HdrEgrOvflCnt = 0;
+ cntrs->RxP11HdrEgrOvflCnt = 0;
+ cntrs->RxP12HdrEgrOvflCnt = 0;
+ cntrs->RxP13HdrEgrOvflCnt = 0;
+ cntrs->RxP14HdrEgrOvflCnt = 0;
+ cntrs->RxP15HdrEgrOvflCnt = 0;
+ cntrs->RxP16HdrEgrOvflCnt = 0;
+ cntrs->IBStatusChangeCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(IBStatusChangeCnt));
+ cntrs->IBLinkErrRecoveryCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(IBLinkErrRecoveryCnt));
+ cntrs->IBLinkDownedCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(IBLinkDownedCnt));
+ cntrs->IBSymbolErrCnt =
+ ipath_snap_cntr(dd, IPATH_CREG_OFFSET(IBSymbolErrCnt));
+ cntrs->RxVL15DroppedPktCnt = 0;
+ cntrs->RxOtherLocalPhyErrCnt = 0;
+ cntrs->PcieRetryBufDiagQwordCnt = 0;
+ cntrs->ExcessBufferOvflCnt = dd->ipath_overrun_thresh_errs;
+ cntrs->LocalLinkIntegrityErrCnt = dd->ipath_lli_errs;
+ cntrs->RxVlErrCnt = 0;
+ cntrs->RxDlidFltrCnt = 0;
+}
+
+
+/* no interrupt fallback for these chips */
+static int ipath_pe_nointr_fallback(struct ipath_devdata *dd)
+{
+ return 0;
+}
+
+
/*
- * On platforms using this chip, and not having ordered WC stores, we
- * can get TXE parity errors due to speculative reads to the PIO buffers,
- * and this, due to a chip bug can result in (many) false parity error
- * reports. So it's a debug print on those, and an info print on systems
- * where the speculative reads don't occur.
- * Because we can get lots of false errors, we have no upper limit
- * on recovery attempts on those platforms.
+ * reset the XGXS (between serdes and IBC). Slightly less intrusive
+ * than resetting the IBC or external link state, and useful in some
+ * cases to cause some retraining. To do this right, we reset IBC
+ * as well.
*/
-static int ipath_pe_txe_recover(struct ipath_devdata *dd)
+static void ipath_pe_xgxs_reset(struct ipath_devdata *dd)
{
- if (ipath_unordered_wc())
- ipath_dbg("Recovering from TXE PIO parity error\n");
- else {
- int cnt = ++ipath_stats.sps_txeparity;
- if (cnt >= IPATH_MAX_PARITY_ATTEMPTS) {
- if (cnt == IPATH_MAX_PARITY_ATTEMPTS)
- ipath_dev_err(dd,
- "Too many attempts to recover from "
- "TXE parity, giving up\n");
- return 0;
- }
- dev_info(&dd->pcidev->dev,
- "Recovering from TXE PIO parity error\n");
+ u64 val, prev_val;
+
+ prev_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig);
+ val = prev_val | INFINIPATH_XGXS_RESET;
+ prev_val &= ~INFINIPATH_XGXS_RESET; /* be sure */
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+ dd->ipath_control & ~INFINIPATH_C_LINKENABLE);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val);
+ ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, prev_val);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
+ dd->ipath_control);
+}
+
+
+static int ipath_pe_get_ib_cfg(struct ipath_devdata *dd, int which)
+{
+ int ret;
+
+ switch (which) {
+ case IPATH_IB_CFG_LWID:
+ ret = dd->ipath_link_width_active;
+ break;
+ case IPATH_IB_CFG_SPD:
+ ret = dd->ipath_link_speed_active;
+ break;
+ case IPATH_IB_CFG_LWID_ENB:
+ ret = dd->ipath_link_width_enabled;
+ break;
+ case IPATH_IB_CFG_SPD_ENB:
+ ret = dd->ipath_link_speed_enabled;
+ break;
+ default:
+ ret = -ENOTSUPP;
+ break;
}
- return 1;
+ return ret;
+}
+
+
+/* we assume range checking is already done, if needed */
+static int ipath_pe_set_ib_cfg(struct ipath_devdata *dd, int which, u32 val)
+{
+ int ret = 0;
+
+ if (which == IPATH_IB_CFG_LWID_ENB)
+ dd->ipath_link_width_enabled = val;
+ else if (which == IPATH_IB_CFG_SPD_ENB)
+ dd->ipath_link_speed_enabled = val;
+ else
+ ret = -ENOTSUPP;
+ return ret;
}
+static void ipath_pe_config_jint(struct ipath_devdata *dd, u16 a, u16 b)
+{
+}
+
+
+static int ipath_pe_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
+{
+ ipath_setup_pe_setextled(dd, ipath_ib_linkstate(dd, ibcs),
+ ipath_ib_linktrstate(dd, ibcs));
+ return 0;
+}
+
+
/**
* ipath_init_iba6120_funcs - set up the chip-specific function pointers
* @dd: the infinipath device
@@ -1407,7 +1666,7 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *dd)
dd->ipath_f_bringup_serdes = ipath_pe_bringup_serdes;
dd->ipath_f_clear_tids = ipath_pe_clear_tids;
/*
- * this may get changed after we read the chip revision,
+ * _f_put_tid may get changed after we read the chip revision,
* but we start with the safe version for all revs
*/
dd->ipath_f_put_tid = ipath_pe_put_tid;
@@ -1415,17 +1674,19 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *dd)
dd->ipath_f_setextled = ipath_setup_pe_setextled;
dd->ipath_f_get_base_info = ipath_pe_get_base_info;
dd->ipath_f_free_irq = ipath_pe_free_irq;
-
- /* initialize chip-specific variables */
dd->ipath_f_tidtemplate = ipath_pe_tidtemplate;
+ dd->ipath_f_intr_fallback = ipath_pe_nointr_fallback;
+ dd->ipath_f_xgxs_reset = ipath_pe_xgxs_reset;
+ dd->ipath_f_get_msgheader = ipath_pe_get_msgheader;
+ dd->ipath_f_config_ports = ipath_pe_config_ports;
+ dd->ipath_f_read_counters = ipath_pe_read_counters;
+ dd->ipath_f_get_ib_cfg = ipath_pe_get_ib_cfg;
+ dd->ipath_f_set_ib_cfg = ipath_pe_set_ib_cfg;
+ dd->ipath_f_config_jint = ipath_pe_config_jint;
+ dd->ipath_f_ib_updown = ipath_pe_ib_updown;
- /*
- * setup the register offsets, since they are different for each
- * chip
- */
- dd->ipath_kregs = &ipath_pe_kregs;
- dd->ipath_cregs = &ipath_pe_cregs;
+ /* initialize chip-specific variables */
ipath_init_pe_variables(dd);
}
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_init_chip.c b/trunk/drivers/infiniband/hw/ipath/ipath_init_chip.c
index 9dd0bacf8461..4471674975cd 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -91,7 +91,7 @@ static int create_port0_egr(struct ipath_devdata *dd)
struct ipath_skbinfo *skbinfo;
int ret;
- egrcnt = dd->ipath_rcvegrcnt;
+ egrcnt = dd->ipath_p0_rcvegrcnt;
skbinfo = vmalloc(sizeof(*dd->ipath_port0_skbinfo) * egrcnt);
if (skbinfo == NULL) {
@@ -244,8 +244,7 @@ static int init_chip_first(struct ipath_devdata *dd,
* cfgports. We do still check and report a difference, if
* not same (should be impossible).
*/
- dd->ipath_portcnt =
- ipath_read_kreg32(dd, dd->ipath_kregs->kr_portcnt);
+ dd->ipath_f_config_ports(dd, ipath_cfgports);
if (!ipath_cfgports)
dd->ipath_cfgports = dd->ipath_portcnt;
else if (ipath_cfgports <= dd->ipath_portcnt) {
@@ -272,22 +271,7 @@ static int init_chip_first(struct ipath_devdata *dd,
goto done;
}
- dd->ipath_lastegrheads = kzalloc(sizeof(*dd->ipath_lastegrheads)
- * dd->ipath_cfgports,
- GFP_KERNEL);
- dd->ipath_lastrcvhdrqtails =
- kzalloc(sizeof(*dd->ipath_lastrcvhdrqtails)
- * dd->ipath_cfgports, GFP_KERNEL);
-
- if (!dd->ipath_lastegrheads || !dd->ipath_lastrcvhdrqtails) {
- ipath_dev_err(dd, "Unable to allocate head arrays, "
- "failing\n");
- ret = -ENOMEM;
- goto done;
- }
-
pd = create_portdata0(dd);
-
if (!pd) {
ipath_dev_err(dd, "Unable to allocate portdata for port "
"0, failing\n");
@@ -345,10 +329,10 @@ static int init_chip_first(struct ipath_devdata *dd,
dd->ipath_piobcnt2k, dd->ipath_pio2kbase);
spin_lock_init(&dd->ipath_tid_lock);
-
+ spin_lock_init(&dd->ipath_sendctrl_lock);
spin_lock_init(&dd->ipath_gpio_lock);
spin_lock_init(&dd->ipath_eep_st_lock);
- sema_init(&dd->ipath_eep_sem, 1);
+ mutex_init(&dd->ipath_eep_lock);
done:
*pdp = pd;
@@ -372,9 +356,9 @@ static int init_chip_reset(struct ipath_devdata *dd,
*pdp = dd->ipath_pd[0];
/* ensure chip does no sends or receives while we re-initialize */
dd->ipath_control = dd->ipath_sendctrl = dd->ipath_rcvctrl = 0U;
- ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, 0);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, 0);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_control, 0);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, dd->ipath_rcvctrl);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_control, dd->ipath_control);
rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_portcnt);
if (dd->ipath_portcnt != rtmp)
@@ -487,6 +471,7 @@ static void enable_chip(struct ipath_devdata *dd,
struct ipath_portdata *pd, int reinit)
{
u32 val;
+ unsigned long flags;
int i;
if (!reinit)
@@ -495,19 +480,21 @@ static void enable_chip(struct ipath_devdata *dd,
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
/* Enable PIO send, and update of PIOavail regs to memory. */
dd->ipath_sendctrl = INFINIPATH_S_PIOENABLE |
INFINIPATH_S_PIOBUFAVAILUPD;
- ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
- dd->ipath_sendctrl);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
/*
* enable port 0 receive, and receive interrupt. other ports
* done as user opens and inits them.
*/
- dd->ipath_rcvctrl = INFINIPATH_R_TAILUPD |
- (1ULL << INFINIPATH_R_PORTENABLE_SHIFT) |
- (1ULL << INFINIPATH_R_INTRAVAIL_SHIFT);
+ dd->ipath_rcvctrl = (1ULL << dd->ipath_r_tailupd_shift) |
+ (1ULL << dd->ipath_r_portenable_shift) |
+ (1ULL << dd->ipath_r_intravail_shift);
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
@@ -523,12 +510,11 @@ static void enable_chip(struct ipath_devdata *dd,
*/
val = ipath_read_ureg32(dd, ur_rcvegrindextail, 0);
(void)ipath_write_ureg(dd, ur_rcvegrindexhead, val, 0);
- dd->ipath_port0head = ipath_read_ureg32(dd, ur_rcvhdrtail, 0);
/* Initialize so we interrupt on next packet received */
(void)ipath_write_ureg(dd, ur_rcvhdrhead,
dd->ipath_rhdrhead_intr_off |
- dd->ipath_port0head, 0);
+ dd->ipath_pd[0]->port_head, 0);
/*
* by now pioavail updates to memory should have occurred, so
@@ -542,12 +528,8 @@ static void enable_chip(struct ipath_devdata *dd,
/*
* Chip Errata bug 6641; even and odd qwords>3 are swapped.
*/
- if (i > 3) {
- if (i & 1)
- val = dd->ipath_pioavailregs_dma[i - 1];
- else
- val = dd->ipath_pioavailregs_dma[i + 1];
- }
+ if (i > 3 && (dd->ipath_flags & IPATH_SWAP_PIOBUFS))
+ val = dd->ipath_pioavailregs_dma[i ^ 1];
else
val = dd->ipath_pioavailregs_dma[i];
dd->ipath_pioavailshadow[i] = le64_to_cpu(val);
@@ -690,12 +672,13 @@ static int init_housekeeping(struct ipath_devdata *dd,
*/
int ipath_init_chip(struct ipath_devdata *dd, int reinit)
{
- int ret = 0, i;
+ int ret = 0;
u32 val32, kpiobufs;
u32 piobufs, uports;
u64 val;
struct ipath_portdata *pd = NULL; /* keep gcc4 happy */
gfp_t gfp_flags = GFP_USER | __GFP_COMP;
+ unsigned long flags;
ret = init_housekeeping(dd, &pd, reinit);
if (ret)
@@ -746,7 +729,7 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
kpiobufs = ipath_kpiobufs;
if (kpiobufs + (uports * IPATH_MIN_USER_PORT_BUFCNT) > piobufs) {
- i = (int) piobufs -
+ int i = (int) piobufs -
(int) (uports * IPATH_MIN_USER_PORT_BUFCNT);
if (i < 0)
i = 0;
@@ -827,8 +810,12 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear,
~0ULL&~INFINIPATH_HWE_MEMBISTFAILED);
ipath_write_kreg(dd, dd->ipath_kregs->kr_control, 0ULL);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
- INFINIPATH_S_PIOENABLE);
+
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+ dd->ipath_sendctrl = INFINIPATH_S_PIOENABLE;
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
/*
* before error clears, since we expect serdes pll errors during
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_intr.c b/trunk/drivers/infiniband/hw/ipath/ipath_intr.c
index c61f9da2964a..92e58c921522 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_intr.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_intr.c
@@ -683,7 +683,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
for (i = 0; i < dd->ipath_cfgports; i++) {
struct ipath_portdata *pd = dd->ipath_pd[i];
if (i == 0) {
- hd = dd->ipath_port0head;
+ hd = pd->port_head;
tl = (u32) le64_to_cpu(
*dd->ipath_hdrqtailptr);
} else if (pd && pd->port_cnt &&
@@ -693,7 +693,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
* except kernel
*/
tl = *(u64 *) pd->port_rcvhdrtail_kvaddr;
- if (tl == dd->ipath_lastrcvhdrqtails[i])
+ if (tl == pd->port_lastrcvhdrqtail)
continue;
hd = ipath_read_ureg32(dd, ur_rcvhdrhead,
i);
@@ -703,7 +703,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
(!hd && tl == dd->ipath_hdrqlast)) {
if (i == 0)
chkerrpkts = 1;
- dd->ipath_lastrcvhdrqtails[i] = tl;
+ pd->port_lastrcvhdrqtail = tl;
pd->port_hdrqfull++;
/* flush hdrqfull so that poll() sees it */
wmb();
@@ -712,6 +712,8 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
}
}
if (errs & INFINIPATH_E_RRCVEGRFULL) {
+ struct ipath_portdata *pd = dd->ipath_pd[0];
+
/*
* since this is of less importance and not likely to
* happen without also getting hdrfull, only count
@@ -719,7 +721,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
* vs user)
*/
ipath_stats.sps_etidfull++;
- if (dd->ipath_port0head !=
+ if (pd->port_head !=
(u32) le64_to_cpu(*dd->ipath_hdrqtailptr))
chkerrpkts = 1;
}
@@ -795,6 +797,7 @@ void ipath_clear_freeze(struct ipath_devdata *dd)
{
int i, im;
__le64 val;
+ unsigned long flags;
/* disable error interrupts, to avoid confusion */
ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, 0ULL);
@@ -813,11 +816,14 @@ void ipath_clear_freeze(struct ipath_devdata *dd)
dd->ipath_control);
/* ensure pio avail updates continue */
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
dd->ipath_sendctrl & ~INFINIPATH_S_PIOBUFAVAILUPD);
ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
- dd->ipath_sendctrl);
+ dd->ipath_sendctrl);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
/*
* We just enabled pioavailupdate, so dma copy is almost certainly
@@ -825,8 +831,8 @@ void ipath_clear_freeze(struct ipath_devdata *dd)
*/
for (i = 0; i < dd->ipath_pioavregs; i++) {
/* deal with 6110 chip bug */
- im = i > 3 ? ((i&1) ? i-1 : i+1) : i;
- val = ipath_read_kreg64(dd, (0x1000/sizeof(u64))+im);
+ im = i > 3 ? i ^ 1 : i;
+ val = ipath_read_kreg64(dd, (0x1000 / sizeof(u64)) + im);
dd->ipath_pioavailregs_dma[i] = dd->ipath_pioavailshadow[i]
= le64_to_cpu(val);
}
@@ -849,7 +855,7 @@ void ipath_clear_freeze(struct ipath_devdata *dd)
/* this is separate to allow for better optimization of ipath_intr() */
-static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp)
+static noinline void ipath_bad_intr(struct ipath_devdata *dd, u32 *unexpectp)
{
/*
* sometimes happen during driver init and unload, don't want
@@ -877,7 +883,7 @@ static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp)
dd->ipath_f_free_irq(dd);
}
}
- if (ipath_read_kreg32(dd, dd->ipath_kregs->kr_intmask)) {
+ if (ipath_read_ireg(dd, dd->ipath_kregs->kr_intmask)) {
ipath_dev_err(dd, "%u unexpected interrupts, "
"disabling interrupts completely\n",
*unexpectp);
@@ -892,7 +898,7 @@ static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp)
"ignoring\n");
}
-static void ipath_bad_regread(struct ipath_devdata *dd)
+static noinline void ipath_bad_regread(struct ipath_devdata *dd)
{
static int allbits;
@@ -920,31 +926,9 @@ static void ipath_bad_regread(struct ipath_devdata *dd)
}
}
-static void handle_port_pioavail(struct ipath_devdata *dd)
-{
- u32 i;
- /*
- * start from port 1, since for now port 0 is never using
- * wait_event for PIO
- */
- for (i = 1; dd->ipath_portpiowait && i < dd->ipath_cfgports; i++) {
- struct ipath_portdata *pd = dd->ipath_pd[i];
-
- if (pd && pd->port_cnt &&
- dd->ipath_portpiowait & (1U << i)) {
- clear_bit(i, &dd->ipath_portpiowait);
- if (test_bit(IPATH_PORT_WAITING_PIO,
- &pd->port_flag)) {
- clear_bit(IPATH_PORT_WAITING_PIO,
- &pd->port_flag);
- wake_up_interruptible(&pd->port_wait);
- }
- }
- }
-}
-
static void handle_layer_pioavail(struct ipath_devdata *dd)
{
+ unsigned long flags;
int ret;
ret = ipath_ib_piobufavail(dd->verbs_dev);
@@ -953,9 +937,12 @@ static void handle_layer_pioavail(struct ipath_devdata *dd)
return;
set:
- set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+ dd->ipath_sendctrl |= INFINIPATH_S_PIOINTBUFAVAIL;
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
dd->ipath_sendctrl);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
}
/*
@@ -969,7 +956,15 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
int i;
int rcvdint = 0;
- /* test_bit below needs this... */
+ /*
+ * test_and_clear_bit(IPATH_PORT_WAITING_RCV) and
+ * test_and_clear_bit(IPATH_PORT_WAITING_URG) below
+ * would both like timely updates of the bits so that
+ * we don't pass them by unnecessarily. the rmb()
+ * here ensures that we see them promptly -- the
+ * corresponding wmb()'s are in ipath_poll_urgent()
+ * and ipath_poll_next()...
+ */
rmb();
portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &
dd->ipath_i_rcvavail_mask)
@@ -980,7 +975,7 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
if (portr & (1 << i) && pd && pd->port_cnt) {
if (test_and_clear_bit(IPATH_PORT_WAITING_RCV,
&pd->port_flag)) {
- clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT,
+ clear_bit(i + dd->ipath_r_intravail_shift,
&dd->ipath_rcvctrl);
wake_up_interruptible(&pd->port_wait);
rcvdint = 1;
@@ -1039,7 +1034,7 @@ irqreturn_t ipath_intr(int irq, void *data)
goto bail;
}
- istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus);
+ istat = ipath_read_ireg(dd, dd->ipath_kregs->kr_intstatus);
if (unlikely(!istat)) {
ipath_stats.sps_nullintr++;
@@ -1180,7 +1175,7 @@ irqreturn_t ipath_intr(int irq, void *data)
* for receive are at the bottom.
*/
if (chk0rcv) {
- ipath_kreceive(dd);
+ ipath_kreceive(dd->ipath_pd[0]);
istat &= ~port0rbits;
}
@@ -1191,12 +1186,14 @@ irqreturn_t ipath_intr(int irq, void *data)
handle_urcv(dd, istat);
if (istat & INFINIPATH_I_SPIOBUFAVAIL) {
- clear_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+ dd->ipath_sendctrl &= ~INFINIPATH_S_PIOINTBUFAVAIL;
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
dd->ipath_sendctrl);
-
- if (dd->ipath_portpiowait)
- handle_port_pioavail(dd);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
handle_layer_pioavail(dd);
}
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_kernel.h b/trunk/drivers/infiniband/hw/ipath/ipath_kernel.h
index bb1dc075f1d1..4cc0f95ea877 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -41,6 +41,7 @@
#include
#include
#include
+#include
#include
#include
@@ -140,6 +141,11 @@ struct ipath_portdata {
u32 port_pionowait;
/* total number of rcvhdrqfull errors */
u32 port_hdrqfull;
+ /*
+ * Used to suppress multiple instances of same
+ * port staying stuck at same point.
+ */
+ u32 port_lastrcvhdrqtail;
/* saved total number of rcvhdrqfull errors for poll edge trigger */
u32 port_hdrqfull_poll;
/* total number of polled urgent packets */
@@ -148,6 +154,7 @@ struct ipath_portdata {
u32 port_urgent_poll;
/* pid of process using this port */
pid_t port_pid;
+ pid_t port_subpid[INFINIPATH_MAX_SUBPORT];
/* same size as task_struct .comm[] */
char port_comm[16];
/* pkeys set by this use of this port */
@@ -166,6 +173,8 @@ struct ipath_portdata {
u32 active_slaves;
/* Type of packets or conditions we want to poll for */
u16 poll_type;
+ /* port rcvhdrq head offset */
+ u32 port_head;
};
struct sk_buff;
@@ -182,6 +191,22 @@ struct ipath_skbinfo {
dma_addr_t phys;
};
+/*
+ * Possible IB config parameters for ipath_f_get/set_ib_cfg()
+ */
+#define IPATH_IB_CFG_LIDLMC 0 /* Get/set LID (LS16b) and Mask (MS16b) */
+#define IPATH_IB_CFG_HRTBT 1 /* Get/set Heartbeat off/enable/auto */
+#define IPATH_IB_HRTBT_ON 3 /* Heartbeat enabled, sent every 100msec */
+#define IPATH_IB_HRTBT_OFF 0 /* Heartbeat off */
+#define IPATH_IB_CFG_LWID_ENB 2 /* Get/set allowed Link-width */
+#define IPATH_IB_CFG_LWID 3 /* Get currently active Link-width */
+#define IPATH_IB_CFG_SPD_ENB 4 /* Get/set allowed Link speeds */
+#define IPATH_IB_CFG_SPD 5 /* Get current Link spd */
+#define IPATH_IB_CFG_RXPOL_ENB 6 /* Get/set Auto-RX-polarity enable */
+#define IPATH_IB_CFG_LREV_ENB 7 /* Get/set Auto-Lane-reversal enable */
+#define IPATH_IB_CFG_LINKLATENCY 8 /* Get Auto-Lane-reversal enable */
+
+
struct ipath_devdata {
struct list_head ipath_list;
@@ -222,6 +247,8 @@ struct ipath_devdata {
struct _ipath_layer ipath_layer;
/* setup intr */
int (*ipath_f_intrsetup)(struct ipath_devdata *);
+ /* fallback to alternate interrupt type if possible */
+ int (*ipath_f_intr_fallback)(struct ipath_devdata *);
/* setup on-chip bus config */
int (*ipath_f_bus)(struct ipath_devdata *, struct pci_dev *);
/* hard reset chip */
@@ -244,6 +271,18 @@ struct ipath_devdata {
int (*ipath_f_get_base_info)(struct ipath_portdata *, void *);
/* free irq */
void (*ipath_f_free_irq)(struct ipath_devdata *);
+ struct ipath_message_header *(*ipath_f_get_msgheader)
+ (struct ipath_devdata *, __le32 *);
+ void (*ipath_f_config_ports)(struct ipath_devdata *, ushort);
+ int (*ipath_f_get_ib_cfg)(struct ipath_devdata *, int);
+ int (*ipath_f_set_ib_cfg)(struct ipath_devdata *, int, u32);
+ void (*ipath_f_config_jint)(struct ipath_devdata *, u16 , u16);
+ void (*ipath_f_read_counters)(struct ipath_devdata *,
+ struct infinipath_counters *);
+ void (*ipath_f_xgxs_reset)(struct ipath_devdata *);
+ /* per chip actions needed for IB Link up/down changes */
+ int (*ipath_f_ib_updown)(struct ipath_devdata *, int, u64);
+
struct ipath_ibdev *verbs_dev;
struct timer_list verbs_timer;
/* total dwords sent (summed from counter) */
@@ -313,21 +352,11 @@ struct ipath_devdata {
* supports, less gives more pio bufs/port, etc.
*/
u32 ipath_cfgports;
- /* port0 rcvhdrq head offset */
- u32 ipath_port0head;
/* count of port 0 hdrqfull errors */
u32 ipath_p0_hdrqfull;
+ /* port 0 number of receive eager buffers */
+ u32 ipath_p0_rcvegrcnt;
- /*
- * (*cfgports) used to suppress multiple instances of same
- * port staying stuck at same point
- */
- u32 *ipath_lastrcvhdrqtails;
- /*
- * (*cfgports) used to suppress multiple instances of same
- * port staying stuck at same point
- */
- u32 *ipath_lastegrheads;
/*
* index of last piobuffer we used. Speeds up searching, by
* starting at this point. Doesn't matter if multiple cpu's use and
@@ -367,14 +396,15 @@ struct ipath_devdata {
unsigned long ipath_wc_len;
/* ref count for each pkey */
atomic_t ipath_pkeyrefs[4];
- /* shadow copy of all exptids physaddr; used only by funcsim */
- u64 *ipath_tidsimshadow;
/* shadow copy of struct page *'s for exp tid pages */
struct page **ipath_pageshadow;
/* shadow copy of dma handles for exp tid pages */
dma_addr_t *ipath_physshadow;
- /* lock to workaround chip bug 9437 */
+ u64 __iomem *ipath_egrtidbase;
+ /* lock to workaround chip bug 9437 and others */
+ spinlock_t ipath_kernel_tid_lock;
spinlock_t ipath_tid_lock;
+ spinlock_t ipath_sendctrl_lock;
/*
* IPATH_STATUS_*,
@@ -395,6 +425,8 @@ struct ipath_devdata {
void *ipath_dummy_hdrq; /* used after port close */
dma_addr_t ipath_dummy_hdrq_phys;
+ unsigned long ipath_ureg_align; /* user register alignment */
+
/*
* Shadow copies of registers; size indicates read access size.
* Most of them are readonly, but some are write-only register,
@@ -456,8 +488,6 @@ struct ipath_devdata {
unsigned long ipath_rcvctrl;
/* shadow kr_sendctrl */
unsigned long ipath_sendctrl;
- /* ports waiting for PIOavail intr */
- unsigned long ipath_portpiowait;
unsigned long ipath_lastcancel; /* to not count armlaunch after cancel */
/* value we put in kr_rcvhdrcnt */
@@ -550,12 +580,26 @@ struct ipath_devdata {
u8 ipath_minrev;
/* board rev, from ipath_revision */
u8 ipath_boardrev;
+
+ u8 ipath_r_portenable_shift;
+ u8 ipath_r_intravail_shift;
+ u8 ipath_r_tailupd_shift;
+ u8 ipath_r_portcfg_shift;
+
/* unit # of this chip, if present */
int ipath_unit;
/* saved for restore after reset */
u8 ipath_pci_cacheline;
/* LID mask control */
u8 ipath_lmc;
+ /* link width supported */
+ u8 ipath_link_width_supported;
+ /* link speed supported */
+ u8 ipath_link_speed_supported;
+ u8 ipath_link_width_enabled;
+ u8 ipath_link_speed_enabled;
+ u8 ipath_link_width_active;
+ u8 ipath_link_speed_active;
/* Rx Polarity inversion (compensate for ~tx on partner) */
u8 ipath_rx_pol_inv;
@@ -590,6 +634,8 @@ struct ipath_devdata {
*/
u32 ipath_i_rcvavail_mask;
u32 ipath_i_rcvurg_mask;
+ u16 ipath_i_rcvurg_shift;
+ u16 ipath_i_rcvavail_shift;
/*
* Register bits for selecting i2c direction and values, used for
@@ -603,6 +649,29 @@ struct ipath_devdata {
/* lock for doing RMW of shadows/regs for ExtCtrl and GPIO */
spinlock_t ipath_gpio_lock;
+ /*
+ * IB link and linktraining states and masks that vary per chip in
+ * some way. Set at init, to avoid each IB status change interrupt
+ */
+ u8 ibcs_ls_shift;
+ u8 ibcs_lts_mask;
+ u32 ibcs_mask;
+ u32 ib_init;
+ u32 ib_arm;
+ u32 ib_active;
+
+ u16 ipath_rhf_offset; /* offset of RHF within receive header entry */
+
+ /*
+ * shift/mask for linkcmd, linkinitcmd, maxpktlen in ibccontol
+ * reg. Changes for IBA7220
+ */
+ u8 ibcc_lic_mask; /* LinkInitCmd */
+ u8 ibcc_lc_shift; /* LinkCmd */
+ u8 ibcc_mpl_shift; /* Maxpktlen */
+
+ u8 delay_mult;
+
/* used to override LED behavior */
u8 ipath_led_override; /* Substituted for normal value, if non-zero */
u16 ipath_led_override_timeoff; /* delta to next timer event */
@@ -616,7 +685,7 @@ struct ipath_devdata {
/* control access to actual counters, timer */
spinlock_t ipath_eep_st_lock;
/* control high-level access to EEPROM */
- struct semaphore ipath_eep_sem;
+ struct mutex ipath_eep_lock;
/* Below inc'd by ipath_snap_cntrs(), locked by ipath_eep_st_lock */
uint64_t ipath_traffic_wds;
/* active time is kept in seconds, but logged in hours */
@@ -630,6 +699,10 @@ struct ipath_devdata {
* each of the counters to increment.
*/
struct ipath_eep_log_mask ipath_eep_st_masks[IPATH_EEP_LOG_CNT];
+
+ /* interrupt mitigation reload register info */
+ u16 ipath_jint_idle_ticks; /* idle clock ticks */
+ u16 ipath_jint_max_packets; /* max packets across all ports */
};
/* Private data for file operations */
@@ -690,7 +763,7 @@ void ipath_free_pddata(struct ipath_devdata *, struct ipath_portdata *);
int ipath_parse_ushort(const char *str, unsigned short *valp);
-void ipath_kreceive(struct ipath_devdata *);
+void ipath_kreceive(struct ipath_portdata *);
int ipath_setrcvhdrsize(struct ipath_devdata *, unsigned);
int ipath_reset_device(int);
void ipath_get_faststats(unsigned long);
@@ -698,6 +771,8 @@ int ipath_set_linkstate(struct ipath_devdata *, u8);
int ipath_set_mtu(struct ipath_devdata *, u16);
int ipath_set_lid(struct ipath_devdata *, u32, u8);
int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
+void ipath_enable_armlaunch(struct ipath_devdata *);
+void ipath_disable_armlaunch(struct ipath_devdata *);
/* for use in system calls, where we want to know device type, etc. */
#define port_fp(fp) ((struct ipath_filedata *)(fp)->private_data)->pd
@@ -744,9 +819,15 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
* are 64bit */
#define IPATH_32BITCOUNTERS 0x20000
/* can miss port0 rx interrupts */
+ /* Interrupt register is 64 bits */
+#define IPATH_INTREG_64 0x40000
#define IPATH_DISABLED 0x80000 /* administratively disabled */
/* Use GPIO interrupts for new counters */
#define IPATH_GPIO_ERRINTRS 0x100000
+#define IPATH_SWAP_PIOBUFS 0x200000
+ /* Suppress heartbeat, even if turning off loopback */
+#define IPATH_NO_HRTBT 0x1000000
+#define IPATH_HAS_MULT_IB_SPEED 0x8000000
/* Bits in GPIO for the added interrupts */
#define IPATH_GPIO_PORT0_BIT 2
@@ -758,8 +839,6 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
/* portdata flag bit offsets */
/* waiting for a packet to arrive */
#define IPATH_PORT_WAITING_RCV 2
- /* waiting for a PIO buffer to be available */
-#define IPATH_PORT_WAITING_PIO 3
/* master has not finished initializing */
#define IPATH_PORT_MASTER_UNINIT 4
/* waiting for an urgent packet to arrive */
@@ -767,8 +846,6 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
/* free up any allocated data at closes */
void ipath_free_data(struct ipath_portdata *dd);
-int ipath_waitfor_mdio_cmdready(struct ipath_devdata *);
-int ipath_waitfor_complete(struct ipath_devdata *, ipath_kreg, u64, u64 *);
u32 __iomem *ipath_getpiobuf(struct ipath_devdata *, u32 *);
void ipath_init_iba6120_funcs(struct ipath_devdata *);
void ipath_init_iba6110_funcs(struct ipath_devdata *);
@@ -792,33 +869,6 @@ void ipath_set_led_override(struct ipath_devdata *dd, unsigned int val);
*/
#define IPATH_DFLT_RCVHDRSIZE 9
-#define IPATH_MDIO_CMD_WRITE 1
-#define IPATH_MDIO_CMD_READ 2
-#define IPATH_MDIO_CLD_DIV 25 /* to get 2.5 Mhz mdio clock */
-#define IPATH_MDIO_CMDVALID 0x40000000 /* bit 30 */
-#define IPATH_MDIO_DATAVALID 0x80000000 /* bit 31 */
-#define IPATH_MDIO_CTRL_STD 0x0
-
-static inline u64 ipath_mdio_req(int cmd, int dev, int reg, int data)
-{
- return (((u64) IPATH_MDIO_CLD_DIV) << 32) |
- (cmd << 26) |
- (dev << 21) |
- (reg << 16) |
- (data & 0xFFFF);
-}
-
- /* signal and fifo status, in bank 31 */
-#define IPATH_MDIO_CTRL_XGXS_REG_8 0x8
- /* controls loopback, redundancy */
-#define IPATH_MDIO_CTRL_8355_REG_1 0x10
- /* premph, encdec, etc. */
-#define IPATH_MDIO_CTRL_8355_REG_2 0x11
- /* Kchars, etc. */
-#define IPATH_MDIO_CTRL_8355_REG_6 0x15
-#define IPATH_MDIO_CTRL_8355_REG_9 0x18
-#define IPATH_MDIO_CTRL_8355_REG_10 0x1D
-
int ipath_get_user_pages(unsigned long, size_t, struct page **);
void ipath_release_user_pages(struct page **, size_t);
void ipath_release_user_pages_on_close(struct page **, size_t);
@@ -863,7 +913,7 @@ static inline u32 ipath_read_ureg32(const struct ipath_devdata *dd,
return readl(regno + (u64 __iomem *)
(dd->ipath_uregbase +
(char __iomem *)dd->ipath_kregbase +
- dd->ipath_palign * port));
+ dd->ipath_ureg_align * port));
}
/**
@@ -880,7 +930,7 @@ static inline void ipath_write_ureg(const struct ipath_devdata *dd,
{
u64 __iomem *ubase = (u64 __iomem *)
(dd->ipath_uregbase + (char __iomem *) dd->ipath_kregbase +
- dd->ipath_palign * port);
+ dd->ipath_ureg_align * port);
if (dd->ipath_kregbase)
writeq(value, &ubase[regno]);
}
@@ -930,6 +980,53 @@ static inline u32 ipath_read_creg32(const struct ipath_devdata *dd,
(char __iomem *)dd->ipath_kregbase));
}
+static inline void ipath_write_creg(const struct ipath_devdata *dd,
+ ipath_creg regno, u64 value)
+{
+ if (dd->ipath_kregbase)
+ writeq(value, regno + (u64 __iomem *)
+ (dd->ipath_cregbase +
+ (char __iomem *)dd->ipath_kregbase));
+}
+
+static inline void ipath_clear_rcvhdrtail(const struct ipath_portdata *pd)
+{
+ *((u64 *) pd->port_rcvhdrtail_kvaddr) = 0ULL;
+}
+
+static inline u32 ipath_get_rcvhdrtail(const struct ipath_portdata *pd)
+{
+ return (u32) le64_to_cpu(*((volatile __le64 *)
+ pd->port_rcvhdrtail_kvaddr));
+}
+
+static inline u64 ipath_read_ireg(const struct ipath_devdata *dd, ipath_kreg r)
+{
+ return (dd->ipath_flags & IPATH_INTREG_64) ?
+ ipath_read_kreg64(dd, r) : ipath_read_kreg32(dd, r);
+}
+
+/*
+ * from contents of IBCStatus (or a saved copy), return linkstate
+ * Report ACTIVE_DEFER as ACTIVE, because we treat them the same
+ * everywhere, anyway (and should be, for almost all purposes).
+ */
+static inline u32 ipath_ib_linkstate(struct ipath_devdata *dd, u64 ibcs)
+{
+ u32 state = (u32)(ibcs >> dd->ibcs_ls_shift) &
+ INFINIPATH_IBCS_LINKSTATE_MASK;
+ if (state == INFINIPATH_IBCS_L_STATE_ACT_DEFER)
+ state = INFINIPATH_IBCS_L_STATE_ACTIVE;
+ return state;
+}
+
+/* from contents of IBCStatus (or a saved copy), return linktrainingstate */
+static inline u32 ipath_ib_linktrstate(struct ipath_devdata *dd, u64 ibcs)
+{
+ return (u32)(ibcs >> INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) &
+ dd->ibcs_lts_mask;
+}
+
/*
* sysfs interface.
*/
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_keys.c b/trunk/drivers/infiniband/hw/ipath/ipath_keys.c
index 85a4aefc6c03..8f32b17a5eed 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_keys.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_keys.c
@@ -128,9 +128,8 @@ int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
int ret;
/*
- * We use LKEY == zero to mean a physical kmalloc() address.
- * This is a bit of a hack since we rely on dma_map_single()
- * being reversible by calling bus_to_virt().
+ * We use LKEY == zero for kernel virtual addresses
+ * (see ipath_get_dma_mr and ipath_dma.c).
*/
if (sge->lkey == 0) {
struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_mad.c b/trunk/drivers/infiniband/hw/ipath/ipath_mad.c
index 3d1432d1e3f4..d98d5f103700 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_mad.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_mad.c
@@ -934,6 +934,7 @@ static int recv_pma_get_portsamplescontrol(struct ib_perf *pmp,
struct ib_pma_portsamplescontrol *p =
(struct ib_pma_portsamplescontrol *)pmp->data;
struct ipath_ibdev *dev = to_idev(ibdev);
+ struct ipath_cregs const *crp = dev->dd->ipath_cregs;
unsigned long flags;
u8 port_select = p->port_select;
@@ -955,7 +956,10 @@ static int recv_pma_get_portsamplescontrol(struct ib_perf *pmp,
p->counter_width = 4; /* 32 bit counters */
p->counter_mask0_9 = COUNTER_MASK0_9;
spin_lock_irqsave(&dev->pending_lock, flags);
- p->sample_status = dev->pma_sample_status;
+ if (crp->cr_psstat)
+ p->sample_status = ipath_read_creg32(dev->dd, crp->cr_psstat);
+ else
+ p->sample_status = dev->pma_sample_status;
p->sample_start = cpu_to_be32(dev->pma_sample_start);
p->sample_interval = cpu_to_be32(dev->pma_sample_interval);
p->tag = cpu_to_be16(dev->pma_tag);
@@ -975,8 +979,9 @@ static int recv_pma_set_portsamplescontrol(struct ib_perf *pmp,
struct ib_pma_portsamplescontrol *p =
(struct ib_pma_portsamplescontrol *)pmp->data;
struct ipath_ibdev *dev = to_idev(ibdev);
+ struct ipath_cregs const *crp = dev->dd->ipath_cregs;
unsigned long flags;
- u32 start;
+ u8 status;
int ret;
if (pmp->attr_mod != 0 ||
@@ -986,59 +991,67 @@ static int recv_pma_set_portsamplescontrol(struct ib_perf *pmp,
goto bail;
}
- start = be32_to_cpu(p->sample_start);
- if (start != 0) {
- spin_lock_irqsave(&dev->pending_lock, flags);
- if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_DONE) {
- dev->pma_sample_status =
- IB_PMA_SAMPLE_STATUS_STARTED;
- dev->pma_sample_start = start;
- dev->pma_sample_interval =
- be32_to_cpu(p->sample_interval);
- dev->pma_tag = be16_to_cpu(p->tag);
- if (p->counter_select[0])
- dev->pma_counter_select[0] =
- p->counter_select[0];
- if (p->counter_select[1])
- dev->pma_counter_select[1] =
- p->counter_select[1];
- if (p->counter_select[2])
- dev->pma_counter_select[2] =
- p->counter_select[2];
- if (p->counter_select[3])
- dev->pma_counter_select[3] =
- p->counter_select[3];
- if (p->counter_select[4])
- dev->pma_counter_select[4] =
- p->counter_select[4];
- }
- spin_unlock_irqrestore(&dev->pending_lock, flags);
+ spin_lock_irqsave(&dev->pending_lock, flags);
+ if (crp->cr_psstat)
+ status = ipath_read_creg32(dev->dd, crp->cr_psstat);
+ else
+ status = dev->pma_sample_status;
+ if (status == IB_PMA_SAMPLE_STATUS_DONE) {
+ dev->pma_sample_start = be32_to_cpu(p->sample_start);
+ dev->pma_sample_interval = be32_to_cpu(p->sample_interval);
+ dev->pma_tag = be16_to_cpu(p->tag);
+ dev->pma_counter_select[0] = p->counter_select[0];
+ dev->pma_counter_select[1] = p->counter_select[1];
+ dev->pma_counter_select[2] = p->counter_select[2];
+ dev->pma_counter_select[3] = p->counter_select[3];
+ dev->pma_counter_select[4] = p->counter_select[4];
+ if (crp->cr_psstat) {
+ ipath_write_creg(dev->dd, crp->cr_psinterval,
+ dev->pma_sample_interval);
+ ipath_write_creg(dev->dd, crp->cr_psstart,
+ dev->pma_sample_start);
+ } else
+ dev->pma_sample_status = IB_PMA_SAMPLE_STATUS_STARTED;
}
+ spin_unlock_irqrestore(&dev->pending_lock, flags);
+
ret = recv_pma_get_portsamplescontrol(pmp, ibdev, port);
bail:
return ret;
}
-static u64 get_counter(struct ipath_ibdev *dev, __be16 sel)
+static u64 get_counter(struct ipath_ibdev *dev,
+ struct ipath_cregs const *crp,
+ __be16 sel)
{
u64 ret;
switch (sel) {
case IB_PMA_PORT_XMIT_DATA:
- ret = dev->ipath_sword;
+ ret = (crp->cr_psxmitdatacount) ?
+ ipath_read_creg32(dev->dd, crp->cr_psxmitdatacount) :
+ dev->ipath_sword;
break;
case IB_PMA_PORT_RCV_DATA:
- ret = dev->ipath_rword;
+ ret = (crp->cr_psrcvdatacount) ?
+ ipath_read_creg32(dev->dd, crp->cr_psrcvdatacount) :
+ dev->ipath_rword;
break;
case IB_PMA_PORT_XMIT_PKTS:
- ret = dev->ipath_spkts;
+ ret = (crp->cr_psxmitpktscount) ?
+ ipath_read_creg32(dev->dd, crp->cr_psxmitpktscount) :
+ dev->ipath_spkts;
break;
case IB_PMA_PORT_RCV_PKTS:
- ret = dev->ipath_rpkts;
+ ret = (crp->cr_psrcvpktscount) ?
+ ipath_read_creg32(dev->dd, crp->cr_psrcvpktscount) :
+ dev->ipath_rpkts;
break;
case IB_PMA_PORT_XMIT_WAIT:
- ret = dev->ipath_xmit_wait;
+ ret = (crp->cr_psxmitwaitcount) ?
+ ipath_read_creg32(dev->dd, crp->cr_psxmitwaitcount) :
+ dev->ipath_xmit_wait;
break;
default:
ret = 0;
@@ -1053,14 +1066,21 @@ static int recv_pma_get_portsamplesresult(struct ib_perf *pmp,
struct ib_pma_portsamplesresult *p =
(struct ib_pma_portsamplesresult *)pmp->data;
struct ipath_ibdev *dev = to_idev(ibdev);
+ struct ipath_cregs const *crp = dev->dd->ipath_cregs;
+ u8 status;
int i;
memset(pmp->data, 0, sizeof(pmp->data));
p->tag = cpu_to_be16(dev->pma_tag);
- p->sample_status = cpu_to_be16(dev->pma_sample_status);
+ if (crp->cr_psstat)
+ status = ipath_read_creg32(dev->dd, crp->cr_psstat);
+ else
+ status = dev->pma_sample_status;
+ p->sample_status = cpu_to_be16(status);
for (i = 0; i < ARRAY_SIZE(dev->pma_counter_select); i++)
- p->counter[i] = cpu_to_be32(
- get_counter(dev, dev->pma_counter_select[i]));
+ p->counter[i] = (status != IB_PMA_SAMPLE_STATUS_DONE) ? 0 :
+ cpu_to_be32(
+ get_counter(dev, crp, dev->pma_counter_select[i]));
return reply((struct ib_smp *) pmp);
}
@@ -1071,16 +1091,23 @@ static int recv_pma_get_portsamplesresult_ext(struct ib_perf *pmp,
struct ib_pma_portsamplesresult_ext *p =
(struct ib_pma_portsamplesresult_ext *)pmp->data;
struct ipath_ibdev *dev = to_idev(ibdev);
+ struct ipath_cregs const *crp = dev->dd->ipath_cregs;
+ u8 status;
int i;
memset(pmp->data, 0, sizeof(pmp->data));
p->tag = cpu_to_be16(dev->pma_tag);
- p->sample_status = cpu_to_be16(dev->pma_sample_status);
+ if (crp->cr_psstat)
+ status = ipath_read_creg32(dev->dd, crp->cr_psstat);
+ else
+ status = dev->pma_sample_status;
+ p->sample_status = cpu_to_be16(status);
/* 64 bits */
p->extended_width = __constant_cpu_to_be32(0x80000000);
for (i = 0; i < ARRAY_SIZE(dev->pma_counter_select); i++)
- p->counter[i] = cpu_to_be64(
- get_counter(dev, dev->pma_counter_select[i]));
+ p->counter[i] = (status != IB_PMA_SAMPLE_STATUS_DONE) ? 0 :
+ cpu_to_be64(
+ get_counter(dev, crp, dev->pma_counter_select[i]));
return reply((struct ib_smp *) pmp);
}
@@ -1113,6 +1140,8 @@ static int recv_pma_get_portcounters(struct ib_perf *pmp,
dev->z_local_link_integrity_errors;
cntrs.excessive_buffer_overrun_errors -=
dev->z_excessive_buffer_overrun_errors;
+ cntrs.vl15_dropped -= dev->z_vl15_dropped;
+ cntrs.vl15_dropped += dev->n_vl15_dropped;
memset(pmp->data, 0, sizeof(pmp->data));
@@ -1156,10 +1185,10 @@ static int recv_pma_get_portcounters(struct ib_perf *pmp,
cntrs.excessive_buffer_overrun_errors = 0xFUL;
p->lli_ebor_errors = (cntrs.local_link_integrity_errors << 4) |
cntrs.excessive_buffer_overrun_errors;
- if (dev->n_vl15_dropped > 0xFFFFUL)
+ if (cntrs.vl15_dropped > 0xFFFFUL)
p->vl15_dropped = __constant_cpu_to_be16(0xFFFF);
else
- p->vl15_dropped = cpu_to_be16((u16)dev->n_vl15_dropped);
+ p->vl15_dropped = cpu_to_be16((u16)cntrs.vl15_dropped);
if (cntrs.port_xmit_data > 0xFFFFFFFFUL)
p->port_xmit_data = __constant_cpu_to_be32(0xFFFFFFFF);
else
@@ -1262,8 +1291,10 @@ static int recv_pma_set_portcounters(struct ib_perf *pmp,
dev->z_excessive_buffer_overrun_errors =
cntrs.excessive_buffer_overrun_errors;
- if (p->counter_select & IB_PMA_SEL_PORT_VL15_DROPPED)
+ if (p->counter_select & IB_PMA_SEL_PORT_VL15_DROPPED) {
dev->n_vl15_dropped = 0;
+ dev->z_vl15_dropped = cntrs.vl15_dropped;
+ }
if (p->counter_select & IB_PMA_SEL_PORT_XMIT_DATA)
dev->z_port_xmit_data = cntrs.port_xmit_data;
@@ -1434,7 +1465,7 @@ static int process_subn(struct ib_device *ibdev, int mad_flags,
* before checking for other consumers.
* Just tell the caller to process it normally.
*/
- ret = IB_MAD_RESULT_FAILURE;
+ ret = IB_MAD_RESULT_SUCCESS;
goto bail;
default:
smp->status |= IB_SMP_UNSUP_METHOD;
@@ -1516,7 +1547,7 @@ static int process_perf(struct ib_device *ibdev, u8 port_num,
* before checking for other consumers.
* Just tell the caller to process it normally.
*/
- ret = IB_MAD_RESULT_FAILURE;
+ ret = IB_MAD_RESULT_SUCCESS;
goto bail;
default:
pmp->status |= IB_SMP_UNSUP_METHOD;
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_qp.c b/trunk/drivers/infiniband/hw/ipath/ipath_qp.c
index b997ff88401b..80dc623cee40 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_qp.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_qp.c
@@ -387,8 +387,8 @@ int ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err)
struct ib_wc wc;
int ret = 0;
- ipath_dbg("QP%d/%d in error state\n",
- qp->ibqp.qp_num, qp->remote_qpn);
+ ipath_dbg("QP%d/%d in error state (%d)\n",
+ qp->ibqp.qp_num, qp->remote_qpn, err);
spin_lock(&dev->pending_lock);
/* XXX What if its already removed by the timeout code? */
@@ -855,8 +855,6 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
* See ipath_mmap() for details.
*/
if (udata && udata->outlen >= sizeof(__u64)) {
- int err;
-
if (!qp->r_rq.wq) {
__u64 offset = 0;
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_rc.c b/trunk/drivers/infiniband/hw/ipath/ipath_rc.c
index 120a61b03bc4..459e46e2c016 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_rc.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_rc.c
@@ -647,6 +647,7 @@ static void send_rc_ack(struct ipath_qp *qp)
queue_ack:
spin_lock_irqsave(&qp->s_lock, flags);
+ dev->n_rc_qacks++;
qp->s_flags |= IPATH_S_ACK_PENDING;
qp->s_nak_state = qp->r_nak_state;
qp->s_ack_psn = qp->r_ack_psn;
@@ -798,11 +799,13 @@ void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc)
static inline void update_last_psn(struct ipath_qp *qp, u32 psn)
{
- if (qp->s_wait_credit) {
- qp->s_wait_credit = 0;
- tasklet_hi_schedule(&qp->s_task);
+ if (qp->s_last_psn != psn) {
+ qp->s_last_psn = psn;
+ if (qp->s_wait_credit) {
+ qp->s_wait_credit = 0;
+ tasklet_hi_schedule(&qp->s_task);
+ }
}
- qp->s_last_psn = psn;
}
/**
@@ -1653,13 +1656,6 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
case OP(SEND_FIRST):
if (!ipath_get_rwqe(qp, 0)) {
rnr_nak:
- /*
- * A RNR NAK will ACK earlier sends and RDMA writes.
- * Don't queue the NAK if a RDMA read or atomic
- * is pending though.
- */
- if (qp->r_nak_state)
- goto done;
qp->r_nak_state = IB_RNR_NAK | qp->r_min_rnr_timer;
qp->r_ack_psn = qp->r_psn;
goto send_ack;
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_registers.h b/trunk/drivers/infiniband/hw/ipath/ipath_registers.h
index 708eba3165d7..6d2a17f9c1da 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_registers.h
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_registers.h
@@ -82,8 +82,7 @@
/* kr_rcvctrl bits */
#define INFINIPATH_R_PORTENABLE_SHIFT 0
-#define INFINIPATH_R_INTRAVAIL_SHIFT 16
-#define INFINIPATH_R_TAILUPD 0x80000000
+#define INFINIPATH_R_QPMAP_ENABLE (1ULL << 38)
/* kr_intstatus, kr_intclear, kr_intmask bits */
#define INFINIPATH_I_RCVURG_SHIFT 0
@@ -272,20 +271,6 @@
#define INFINIPATH_EXTC_LEDGBLOK_ON 0x00000002ULL
#define INFINIPATH_EXTC_LEDGBLERR_OFF 0x00000001ULL
-/* kr_mdio bits */
-#define INFINIPATH_MDIO_CLKDIV_MASK 0x7FULL
-#define INFINIPATH_MDIO_CLKDIV_SHIFT 32
-#define INFINIPATH_MDIO_COMMAND_MASK 0x7ULL
-#define INFINIPATH_MDIO_COMMAND_SHIFT 26
-#define INFINIPATH_MDIO_DEVADDR_MASK 0x1FULL
-#define INFINIPATH_MDIO_DEVADDR_SHIFT 21
-#define INFINIPATH_MDIO_REGADDR_MASK 0x1FULL
-#define INFINIPATH_MDIO_REGADDR_SHIFT 16
-#define INFINIPATH_MDIO_DATA_MASK 0xFFFFULL
-#define INFINIPATH_MDIO_DATA_SHIFT 0
-#define INFINIPATH_MDIO_CMDVALID 0x0000000040000000ULL
-#define INFINIPATH_MDIO_RDDATAVALID 0x0000000080000000ULL
-
/* kr_partitionkey bits */
#define INFINIPATH_PKEY_SIZE 16
#define INFINIPATH_PKEY_MASK 0xFFFF
@@ -303,8 +288,6 @@
/* kr_xgxsconfig bits */
#define INFINIPATH_XGXS_RESET 0x7ULL
-#define INFINIPATH_XGXS_MDIOADDR_MASK 0xfULL
-#define INFINIPATH_XGXS_MDIOADDR_SHIFT 4
#define INFINIPATH_XGXS_RX_POL_SHIFT 19
#define INFINIPATH_XGXS_RX_POL_MASK 0xfULL
@@ -470,6 +453,20 @@ struct ipath_cregs {
ipath_creg cr_unsupvlcnt;
ipath_creg cr_wordrcvcnt;
ipath_creg cr_wordsendcnt;
+ ipath_creg cr_vl15droppedpktcnt;
+ ipath_creg cr_rxotherlocalphyerrcnt;
+ ipath_creg cr_excessbufferovflcnt;
+ ipath_creg cr_locallinkintegrityerrcnt;
+ ipath_creg cr_rxvlerrcnt;
+ ipath_creg cr_rxdlidfltrcnt;
+ ipath_creg cr_psstat;
+ ipath_creg cr_psstart;
+ ipath_creg cr_psinterval;
+ ipath_creg cr_psrcvdatacount;
+ ipath_creg cr_psrcvpktscount;
+ ipath_creg cr_psxmitdatacount;
+ ipath_creg cr_psxmitpktscount;
+ ipath_creg cr_psxmitwaitcount;
};
#endif /* _IPATH_REGISTERS_H */
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_ruc.c b/trunk/drivers/infiniband/hw/ipath/ipath_ruc.c
index 54c61a972de2..a59bdbd0ed87 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_ruc.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_ruc.c
@@ -98,11 +98,15 @@ void ipath_insert_rnr_queue(struct ipath_qp *qp)
while (qp->s_rnr_timeout >= nqp->s_rnr_timeout) {
qp->s_rnr_timeout -= nqp->s_rnr_timeout;
l = l->next;
- if (l->next == &dev->rnrwait)
+ if (l->next == &dev->rnrwait) {
+ nqp = NULL;
break;
+ }
nqp = list_entry(l->next, struct ipath_qp,
timerwait);
}
+ if (nqp)
+ nqp->s_rnr_timeout -= qp->s_rnr_timeout;
list_add(&qp->timerwait, l);
}
spin_unlock_irqrestore(&dev->pending_lock, flags);
@@ -479,9 +483,14 @@ static void ipath_ruc_loopback(struct ipath_qp *sqp)
static void want_buffer(struct ipath_devdata *dd)
{
- set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dd->ipath_sendctrl_lock, flags);
+ dd->ipath_sendctrl |= INFINIPATH_S_PIOINTBUFAVAIL;
ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
dd->ipath_sendctrl);
+ ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+ spin_unlock_irqrestore(&dd->ipath_sendctrl_lock, flags);
}
/**
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_srq.c b/trunk/drivers/infiniband/hw/ipath/ipath_srq.c
index 2fef36f4b675..f772102e4713 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_srq.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_srq.c
@@ -94,8 +94,8 @@ int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
/**
* ipath_create_srq - create a shared receive queue
* @ibpd: the protection domain of the SRQ to create
- * @attr: the attributes of the SRQ
- * @udata: not used by the InfiniPath verbs driver
+ * @srq_init_attr: the attributes of the SRQ
+ * @udata: data from libipathverbs when creating a user SRQ
*/
struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
struct ib_srq_init_attr *srq_init_attr,
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_stats.c b/trunk/drivers/infiniband/hw/ipath/ipath_stats.c
index f0271415cd5b..d2725cd11bdc 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_stats.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_stats.c
@@ -133,15 +133,16 @@ u64 ipath_snap_cntr(struct ipath_devdata *dd, ipath_creg creg)
static void ipath_qcheck(struct ipath_devdata *dd)
{
static u64 last_tot_hdrqfull;
+ struct ipath_portdata *pd = dd->ipath_pd[0];
size_t blen = 0;
char buf[128];
*buf = 0;
- if (dd->ipath_pd[0]->port_hdrqfull != dd->ipath_p0_hdrqfull) {
+ if (pd->port_hdrqfull != dd->ipath_p0_hdrqfull) {
blen = snprintf(buf, sizeof buf, "port 0 hdrqfull %u",
- dd->ipath_pd[0]->port_hdrqfull -
+ pd->port_hdrqfull -
dd->ipath_p0_hdrqfull);
- dd->ipath_p0_hdrqfull = dd->ipath_pd[0]->port_hdrqfull;
+ dd->ipath_p0_hdrqfull = pd->port_hdrqfull;
}
if (ipath_stats.sps_etidfull != dd->ipath_last_tidfull) {
blen += snprintf(buf + blen, sizeof buf - blen,
@@ -173,7 +174,7 @@ static void ipath_qcheck(struct ipath_devdata *dd)
if (blen)
ipath_dbg("%s\n", buf);
- if (dd->ipath_port0head != (u32)
+ if (pd->port_head != (u32)
le64_to_cpu(*dd->ipath_hdrqtailptr)) {
if (dd->ipath_lastport0rcv_cnt ==
ipath_stats.sps_port0pkts) {
@@ -181,7 +182,7 @@ static void ipath_qcheck(struct ipath_devdata *dd)
"port0 hd=%llx tl=%x; port0pkts %llx\n",
(unsigned long long)
le64_to_cpu(*dd->ipath_hdrqtailptr),
- dd->ipath_port0head,
+ pd->port_head,
(unsigned long long)
ipath_stats.sps_port0pkts);
}
@@ -237,7 +238,7 @@ static void ipath_chk_errormask(struct ipath_devdata *dd)
void ipath_get_faststats(unsigned long opaque)
{
struct ipath_devdata *dd = (struct ipath_devdata *) opaque;
- u32 val;
+ int i;
static unsigned cnt;
unsigned long flags;
u64 traffic_wds;
@@ -321,12 +322,11 @@ void ipath_get_faststats(unsigned long opaque)
/* limit qfull messages to ~one per minute per port */
if ((++cnt & 0x10)) {
- for (val = dd->ipath_cfgports - 1; ((int)val) >= 0;
- val--) {
- if (dd->ipath_lastegrheads[val] != -1)
- dd->ipath_lastegrheads[val] = -1;
- if (dd->ipath_lastrcvhdrqtails[val] != -1)
- dd->ipath_lastrcvhdrqtails[val] = -1;
+ for (i = (int) dd->ipath_cfgports; --i >= 0; ) {
+ struct ipath_portdata *pd = dd->ipath_pd[i];
+
+ if (pd && pd->port_lastrcvhdrqtail != -1)
+ pd->port_lastrcvhdrqtail = -1;
}
}
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_sysfs.c b/trunk/drivers/infiniband/hw/ipath/ipath_sysfs.c
index aa27ca9f03b1..56dfc8a2344c 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_sysfs.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_sysfs.c
@@ -363,6 +363,60 @@ static ssize_t show_unit(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_unit);
}
+static ssize_t show_jint_max_packets(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%hu\n", dd->ipath_jint_max_packets);
+}
+
+static ssize_t store_jint_max_packets(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ u16 v = 0;
+ int ret;
+
+ ret = ipath_parse_ushort(buf, &v);
+ if (ret < 0)
+ ipath_dev_err(dd, "invalid jint_max_packets.\n");
+ else
+ dd->ipath_f_config_jint(dd, dd->ipath_jint_idle_ticks, v);
+
+ return ret;
+}
+
+static ssize_t show_jint_idle_ticks(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%hu\n", dd->ipath_jint_idle_ticks);
+}
+
+static ssize_t store_jint_idle_ticks(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ u16 v = 0;
+ int ret;
+
+ ret = ipath_parse_ushort(buf, &v);
+ if (ret < 0)
+ ipath_dev_err(dd, "invalid jint_idle_ticks.\n");
+ else
+ dd->ipath_f_config_jint(dd, v, dd->ipath_jint_max_packets);
+
+ return ret;
+}
+
#define DEVICE_COUNTER(name, attr) \
static ssize_t show_counter_##name(struct device *dev, \
struct device_attribute *attr, \
@@ -670,6 +724,257 @@ static ssize_t show_logged_errs(struct device *dev,
return count;
}
+/*
+ * New sysfs entries to control various IB config. These all turn into
+ * accesses via ipath_f_get/set_ib_cfg.
+ *
+ * Get/Set heartbeat enable. Or of 1=enabled, 2=auto
+ */
+static ssize_t show_hrtbt_enb(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dd->ipath_f_get_ib_cfg(dd, IPATH_IB_CFG_HRTBT);
+ if (ret >= 0)
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+ return ret;
+}
+
+static ssize_t store_hrtbt_enb(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret, r;
+ u16 val;
+
+ ret = ipath_parse_ushort(buf, &val);
+ if (ret >= 0 && val > 3)
+ ret = -EINVAL;
+ if (ret < 0) {
+ ipath_dev_err(dd, "attempt to set invalid Heartbeat enable\n");
+ goto bail;
+ }
+
+ /*
+ * Set the "intentional" heartbeat enable per either of
+ * "Enable" and "Auto", as these are normally set together.
+ * This bit is consulted when leaving loopback mode,
+ * because entering loopback mode overrides it and automatically
+ * disables heartbeat.
+ */
+ r = dd->ipath_f_set_ib_cfg(dd, IPATH_IB_CFG_HRTBT, val);
+ if (r < 0)
+ ret = r;
+ else if (val == IPATH_IB_HRTBT_OFF)
+ dd->ipath_flags |= IPATH_NO_HRTBT;
+ else
+ dd->ipath_flags &= ~IPATH_NO_HRTBT;
+
+bail:
+ return ret;
+}
+
+/*
+ * Get/Set Link-widths enabled. Or of 1=1x, 2=4x (this is human/IB centric,
+ * _not_ the particular encoding of any given chip)
+ */
+static ssize_t show_lwid_enb(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dd->ipath_f_get_ib_cfg(dd, IPATH_IB_CFG_LWID_ENB);
+ if (ret >= 0)
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+ return ret;
+}
+
+static ssize_t store_lwid_enb(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret, r;
+ u16 val;
+
+ ret = ipath_parse_ushort(buf, &val);
+ if (ret >= 0 && (val == 0 || val > 3))
+ ret = -EINVAL;
+ if (ret < 0) {
+ ipath_dev_err(dd,
+ "attempt to set invalid Link Width (enable)\n");
+ goto bail;
+ }
+
+ r = dd->ipath_f_set_ib_cfg(dd, IPATH_IB_CFG_LWID_ENB, val);
+ if (r < 0)
+ ret = r;
+
+bail:
+ return ret;
+}
+
+/* Get current link width */
+static ssize_t show_lwid(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dd->ipath_f_get_ib_cfg(dd, IPATH_IB_CFG_LWID);
+ if (ret >= 0)
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+ return ret;
+}
+
+/*
+ * Get/Set Link-speeds enabled. Or of 1=SDR 2=DDR.
+ */
+static ssize_t show_spd_enb(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dd->ipath_f_get_ib_cfg(dd, IPATH_IB_CFG_SPD_ENB);
+ if (ret >= 0)
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+ return ret;
+}
+
+static ssize_t store_spd_enb(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret, r;
+ u16 val;
+
+ ret = ipath_parse_ushort(buf, &val);
+ if (ret >= 0 && (val == 0 || val > (IPATH_IB_SDR | IPATH_IB_DDR)))
+ ret = -EINVAL;
+ if (ret < 0) {
+ ipath_dev_err(dd,
+ "attempt to set invalid Link Speed (enable)\n");
+ goto bail;
+ }
+
+ r = dd->ipath_f_set_ib_cfg(dd, IPATH_IB_CFG_SPD_ENB, val);
+ if (r < 0)
+ ret = r;
+
+bail:
+ return ret;
+}
+
+/* Get current link speed */
+static ssize_t show_spd(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dd->ipath_f_get_ib_cfg(dd, IPATH_IB_CFG_SPD);
+ if (ret >= 0)
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+ return ret;
+}
+
+/*
+ * Get/Set RX polarity-invert enable. 0=no, 1=yes.
+ */
+static ssize_t show_rx_polinv_enb(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dd->ipath_f_get_ib_cfg(dd, IPATH_IB_CFG_RXPOL_ENB);
+ if (ret >= 0)
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+ return ret;
+}
+
+static ssize_t store_rx_polinv_enb(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret, r;
+ u16 val;
+
+ ret = ipath_parse_ushort(buf, &val);
+ if (ret < 0 || val > 1)
+ goto invalid;
+
+ r = dd->ipath_f_set_ib_cfg(dd, IPATH_IB_CFG_RXPOL_ENB, val);
+ if (r < 0) {
+ ret = r;
+ goto bail;
+ }
+
+ goto bail;
+invalid:
+ ipath_dev_err(dd, "attempt to set invalid Rx Polarity (enable)\n");
+bail:
+ return ret;
+}
+/*
+ * Get/Set RX lane-reversal enable. 0=no, 1=yes.
+ */
+static ssize_t show_lanerev_enb(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dd->ipath_f_get_ib_cfg(dd, IPATH_IB_CFG_LREV_ENB);
+ if (ret >= 0)
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+ return ret;
+}
+
+static ssize_t store_lanerev_enb(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+ int ret, r;
+ u16 val;
+
+ ret = ipath_parse_ushort(buf, &val);
+ if (ret >= 0 && val > 1) {
+ ret = -EINVAL;
+ ipath_dev_err(dd,
+ "attempt to set invalid Lane reversal (enable)\n");
+ goto bail;
+ }
+
+ r = dd->ipath_f_set_ib_cfg(dd, IPATH_IB_CFG_LREV_ENB, val);
+ if (r < 0)
+ ret = r;
+
+bail:
+ return ret;
+}
+
static DRIVER_ATTR(num_units, S_IRUGO, show_num_units, NULL);
static DRIVER_ATTR(version, S_IRUGO, show_version, NULL);
@@ -706,6 +1011,10 @@ static DEVICE_ATTR(unit, S_IRUGO, show_unit, NULL);
static DEVICE_ATTR(rx_pol_inv, S_IWUSR, NULL, store_rx_pol_inv);
static DEVICE_ATTR(led_override, S_IWUSR, NULL, store_led_override);
static DEVICE_ATTR(logged_errors, S_IRUGO, show_logged_errs, NULL);
+static DEVICE_ATTR(jint_max_packets, S_IWUSR | S_IRUGO,
+ show_jint_max_packets, store_jint_max_packets);
+static DEVICE_ATTR(jint_idle_ticks, S_IWUSR | S_IRUGO,
+ show_jint_idle_ticks, store_jint_idle_ticks);
static struct attribute *dev_attributes[] = {
&dev_attr_guid.attr,
@@ -732,6 +1041,34 @@ static struct attribute_group dev_attr_group = {
.attrs = dev_attributes
};
+static DEVICE_ATTR(hrtbt_enable, S_IWUSR | S_IRUGO, show_hrtbt_enb,
+ store_hrtbt_enb);
+static DEVICE_ATTR(link_width_enable, S_IWUSR | S_IRUGO, show_lwid_enb,
+ store_lwid_enb);
+static DEVICE_ATTR(link_width, S_IRUGO, show_lwid, NULL);
+static DEVICE_ATTR(link_speed_enable, S_IWUSR | S_IRUGO, show_spd_enb,
+ store_spd_enb);
+static DEVICE_ATTR(link_speed, S_IRUGO, show_spd, NULL);
+static DEVICE_ATTR(rx_pol_inv_enable, S_IWUSR | S_IRUGO, show_rx_polinv_enb,
+ store_rx_polinv_enb);
+static DEVICE_ATTR(rx_lane_rev_enable, S_IWUSR | S_IRUGO, show_lanerev_enb,
+ store_lanerev_enb);
+
+static struct attribute *dev_ibcfg_attributes[] = {
+ &dev_attr_hrtbt_enable.attr,
+ &dev_attr_link_width_enable.attr,
+ &dev_attr_link_width.attr,
+ &dev_attr_link_speed_enable.attr,
+ &dev_attr_link_speed.attr,
+ &dev_attr_rx_pol_inv_enable.attr,
+ &dev_attr_rx_lane_rev_enable.attr,
+ NULL
+};
+
+static struct attribute_group dev_ibcfg_attr_group = {
+ .attrs = dev_ibcfg_attributes
+};
+
/**
* ipath_expose_reset - create a device reset file
* @dev: the device structure
@@ -770,6 +1107,26 @@ int ipath_device_create_group(struct device *dev, struct ipath_devdata *dd)
if (ret)
goto bail_attrs;
+ if (dd->ipath_flags & IPATH_HAS_MULT_IB_SPEED) {
+ ret = device_create_file(dev, &dev_attr_jint_idle_ticks);
+ if (ret)
+ goto bail_counter;
+ ret = device_create_file(dev, &dev_attr_jint_max_packets);
+ if (ret)
+ goto bail_idle;
+
+ ret = sysfs_create_group(&dev->kobj, &dev_ibcfg_attr_group);
+ if (ret)
+ goto bail_max;
+ }
+
+ return 0;
+
+bail_max:
+ device_remove_file(dev, &dev_attr_jint_max_packets);
+bail_idle:
+ device_remove_file(dev, &dev_attr_jint_idle_ticks);
+bail_counter:
sysfs_remove_group(&dev->kobj, &dev_counter_attr_group);
bail_attrs:
sysfs_remove_group(&dev->kobj, &dev_attr_group);
@@ -780,6 +1137,13 @@ int ipath_device_create_group(struct device *dev, struct ipath_devdata *dd)
void ipath_device_remove_group(struct device *dev, struct ipath_devdata *dd)
{
sysfs_remove_group(&dev->kobj, &dev_counter_attr_group);
+
+ if (dd->ipath_flags & IPATH_HAS_MULT_IB_SPEED) {
+ sysfs_remove_group(&dev->kobj, &dev_ibcfg_attr_group);
+ device_remove_file(dev, &dev_attr_jint_idle_ticks);
+ device_remove_file(dev, &dev_attr_jint_max_packets);
+ }
+
sysfs_remove_group(&dev->kobj, &dev_attr_group);
device_remove_file(dev, &dev_attr_reset);
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_ud.c b/trunk/drivers/infiniband/hw/ipath/ipath_ud.c
index b3df6f3c705e..de67eed08ed0 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_ud.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_ud.c
@@ -301,8 +301,6 @@ int ipath_make_ud_req(struct ipath_qp *qp)
/* header size in 32-bit words LRH+BTH+DETH = (8+12+8)/4. */
qp->s_hdrwords = 7;
- if (wqe->wr.opcode == IB_WR_SEND_WITH_IMM)
- qp->s_hdrwords++;
qp->s_cur_size = wqe->length;
qp->s_cur_sge = &qp->s_sge;
qp->s_wqe = wqe;
@@ -327,6 +325,7 @@ int ipath_make_ud_req(struct ipath_qp *qp)
ohdr = &qp->s_hdr.u.oth;
}
if (wqe->wr.opcode == IB_WR_SEND_WITH_IMM) {
+ qp->s_hdrwords++;
ohdr->u.ud.imm_data = wqe->wr.imm_data;
bth0 = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE << 24;
} else
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_verbs.c b/trunk/drivers/infiniband/hw/ipath/ipath_verbs.c
index c4c998446c7b..32d8f882e56c 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -943,7 +943,7 @@ static int ipath_verbs_send_pio(struct ipath_qp *qp, u32 *hdr, u32 hdrwords,
* ipath_verbs_send - send a packet
* @qp: the QP to send on
* @hdr: the packet header
- * @hdrwords: the number of words in the header
+ * @hdrwords: the number of 32-bit words in the header
* @ss: the SGE to send
* @len: the length of the packet in bytes
*/
@@ -955,7 +955,10 @@ int ipath_verbs_send(struct ipath_qp *qp, struct ipath_ib_header *hdr,
int ret;
u32 dwords = (len + 3) >> 2;
- /* +1 is for the qword padding of pbc */
+ /*
+ * Calculate the send buffer trigger address.
+ * The +1 counts for the pbc control dword following the pbc length.
+ */
plen = hdrwords + dwords + 1;
/* Drop non-VL15 packets if we are not in the active state */
@@ -1130,20 +1133,34 @@ static int ipath_query_device(struct ib_device *ibdev,
return 0;
}
-const u8 ipath_cvt_physportstate[16] = {
- [INFINIPATH_IBCS_LT_STATE_DISABLED] = 3,
- [INFINIPATH_IBCS_LT_STATE_LINKUP] = 5,
- [INFINIPATH_IBCS_LT_STATE_POLLACTIVE] = 2,
- [INFINIPATH_IBCS_LT_STATE_POLLQUIET] = 2,
- [INFINIPATH_IBCS_LT_STATE_SLEEPDELAY] = 1,
- [INFINIPATH_IBCS_LT_STATE_SLEEPQUIET] = 1,
- [INFINIPATH_IBCS_LT_STATE_CFGDEBOUNCE] = 4,
- [INFINIPATH_IBCS_LT_STATE_CFGRCVFCFG] = 4,
- [INFINIPATH_IBCS_LT_STATE_CFGWAITRMT] = 4,
- [INFINIPATH_IBCS_LT_STATE_CFGIDLE] = 4,
- [INFINIPATH_IBCS_LT_STATE_RECOVERRETRAIN] = 6,
- [INFINIPATH_IBCS_LT_STATE_RECOVERWAITRMT] = 6,
- [INFINIPATH_IBCS_LT_STATE_RECOVERIDLE] = 6,
+const u8 ipath_cvt_physportstate[32] = {
+ [INFINIPATH_IBCS_LT_STATE_DISABLED] = IB_PHYSPORTSTATE_DISABLED,
+ [INFINIPATH_IBCS_LT_STATE_LINKUP] = IB_PHYSPORTSTATE_LINKUP,
+ [INFINIPATH_IBCS_LT_STATE_POLLACTIVE] = IB_PHYSPORTSTATE_POLL,
+ [INFINIPATH_IBCS_LT_STATE_POLLQUIET] = IB_PHYSPORTSTATE_POLL,
+ [INFINIPATH_IBCS_LT_STATE_SLEEPDELAY] = IB_PHYSPORTSTATE_SLEEP,
+ [INFINIPATH_IBCS_LT_STATE_SLEEPQUIET] = IB_PHYSPORTSTATE_SLEEP,
+ [INFINIPATH_IBCS_LT_STATE_CFGDEBOUNCE] =
+ IB_PHYSPORTSTATE_CFG_TRAIN,
+ [INFINIPATH_IBCS_LT_STATE_CFGRCVFCFG] =
+ IB_PHYSPORTSTATE_CFG_TRAIN,
+ [INFINIPATH_IBCS_LT_STATE_CFGWAITRMT] =
+ IB_PHYSPORTSTATE_CFG_TRAIN,
+ [INFINIPATH_IBCS_LT_STATE_CFGIDLE] = IB_PHYSPORTSTATE_CFG_TRAIN,
+ [INFINIPATH_IBCS_LT_STATE_RECOVERRETRAIN] =
+ IB_PHYSPORTSTATE_LINK_ERR_RECOVER,
+ [INFINIPATH_IBCS_LT_STATE_RECOVERWAITRMT] =
+ IB_PHYSPORTSTATE_LINK_ERR_RECOVER,
+ [INFINIPATH_IBCS_LT_STATE_RECOVERIDLE] =
+ IB_PHYSPORTSTATE_LINK_ERR_RECOVER,
+ [0x10] = IB_PHYSPORTSTATE_CFG_TRAIN,
+ [0x11] = IB_PHYSPORTSTATE_CFG_TRAIN,
+ [0x12] = IB_PHYSPORTSTATE_CFG_TRAIN,
+ [0x13] = IB_PHYSPORTSTATE_CFG_TRAIN,
+ [0x14] = IB_PHYSPORTSTATE_CFG_TRAIN,
+ [0x15] = IB_PHYSPORTSTATE_CFG_TRAIN,
+ [0x16] = IB_PHYSPORTSTATE_CFG_TRAIN,
+ [0x17] = IB_PHYSPORTSTATE_CFG_TRAIN
};
u32 ipath_get_cr_errpkey(struct ipath_devdata *dd)
@@ -1168,8 +1185,9 @@ static int ipath_query_port(struct ib_device *ibdev,
ibcstat = dd->ipath_lastibcstat;
props->state = ((ibcstat >> 4) & 0x3) + 1;
/* See phys_state_show() */
- props->phys_state = ipath_cvt_physportstate[
- dd->ipath_lastibcstat & 0xf];
+ props->phys_state = /* MEA: assumes shift == 0 */
+ ipath_cvt_physportstate[dd->ipath_lastibcstat &
+ dd->ibcs_lts_mask];
props->port_cap_flags = dev->port_cap_flags;
props->gid_tbl_len = 1;
props->max_msg_sz = 0x80000000;
@@ -1641,6 +1659,7 @@ int ipath_register_ib_device(struct ipath_devdata *dd)
cntrs.local_link_integrity_errors;
idev->z_excessive_buffer_overrun_errors =
cntrs.excessive_buffer_overrun_errors;
+ idev->z_vl15_dropped = cntrs.vl15_dropped;
/*
* The system image GUID is supposed to be the same for all
diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_verbs.h b/trunk/drivers/infiniband/hw/ipath/ipath_verbs.h
index 6ccb54f104a3..3d59736b49b2 100644
--- a/trunk/drivers/infiniband/hw/ipath/ipath_verbs.h
+++ b/trunk/drivers/infiniband/hw/ipath/ipath_verbs.h
@@ -554,6 +554,7 @@ struct ipath_ibdev {
u32 z_pkey_violations; /* starting count for PMA */
u32 z_local_link_integrity_errors; /* starting count for PMA */
u32 z_excessive_buffer_overrun_errors; /* starting count for PMA */
+ u32 z_vl15_dropped; /* starting count for PMA */
u32 n_rc_resends;
u32 n_rc_acks;
u32 n_rc_qacks;
@@ -598,6 +599,7 @@ struct ipath_verbs_counters {
u64 port_rcv_packets;
u32 local_link_integrity_errors;
u32 excessive_buffer_overrun_errors;
+ u32 vl15_dropped;
};
static inline struct ipath_mr *to_imr(struct ib_mr *ibmr)
@@ -830,7 +832,17 @@ unsigned ipath_get_pkey(struct ipath_devdata *, unsigned);
extern const enum ib_wc_opcode ib_ipath_wc_opcode[];
+/*
+ * Below converts HCA-specific LinkTrainingState to IB PhysPortState
+ * values.
+ */
extern const u8 ipath_cvt_physportstate[];
+#define IB_PHYSPORTSTATE_SLEEP 1
+#define IB_PHYSPORTSTATE_POLL 2
+#define IB_PHYSPORTSTATE_DISABLED 3
+#define IB_PHYSPORTSTATE_CFG_TRAIN 4
+#define IB_PHYSPORTSTATE_LINKUP 5
+#define IB_PHYSPORTSTATE_LINK_ERR_RECOVER 6
extern const int ib_ipath_state_ops[];
diff --git a/trunk/drivers/infiniband/hw/mlx4/cq.c b/trunk/drivers/infiniband/hw/mlx4/cq.c
index 9d32c49cc651..7950aa6e8184 100644
--- a/trunk/drivers/infiniband/hw/mlx4/cq.c
+++ b/trunk/drivers/infiniband/hw/mlx4/cq.c
@@ -313,6 +313,7 @@ static int mlx4_ib_poll_one(struct mlx4_ib_cq *cq,
struct mlx4_ib_srq *srq;
int is_send;
int is_error;
+ u32 g_mlpath_rqpn;
u16 wqe_ctr;
cqe = next_cqe_sw(cq);
@@ -426,10 +427,10 @@ static int mlx4_ib_poll_one(struct mlx4_ib_cq *cq,
wc->slid = be16_to_cpu(cqe->rlid);
wc->sl = cqe->sl >> 4;
- wc->src_qp = be32_to_cpu(cqe->g_mlpath_rqpn) & 0xffffff;
- wc->dlid_path_bits = (be32_to_cpu(cqe->g_mlpath_rqpn) >> 24) & 0x7f;
- wc->wc_flags |= be32_to_cpu(cqe->g_mlpath_rqpn) & 0x80000000 ?
- IB_WC_GRH : 0;
+ g_mlpath_rqpn = be32_to_cpu(cqe->g_mlpath_rqpn);
+ wc->src_qp = g_mlpath_rqpn & 0xffffff;
+ wc->dlid_path_bits = (g_mlpath_rqpn >> 24) & 0x7f;
+ wc->wc_flags |= g_mlpath_rqpn & 0x80000000 ? IB_WC_GRH : 0;
wc->pkey_index = be32_to_cpu(cqe->immed_rss_invalid) & 0x7f;
}
diff --git a/trunk/drivers/infiniband/hw/mthca/mthca_dev.h b/trunk/drivers/infiniband/hw/mthca/mthca_dev.h
index 15aa32eb78b6..7bbdd1f4e6c7 100644
--- a/trunk/drivers/infiniband/hw/mthca/mthca_dev.h
+++ b/trunk/drivers/infiniband/hw/mthca/mthca_dev.h
@@ -60,13 +60,12 @@
enum {
MTHCA_FLAG_DDR_HIDDEN = 1 << 1,
MTHCA_FLAG_SRQ = 1 << 2,
- MTHCA_FLAG_MSI = 1 << 3,
- MTHCA_FLAG_MSI_X = 1 << 4,
- MTHCA_FLAG_NO_LAM = 1 << 5,
- MTHCA_FLAG_FMR = 1 << 6,
- MTHCA_FLAG_MEMFREE = 1 << 7,
- MTHCA_FLAG_PCIE = 1 << 8,
- MTHCA_FLAG_SINAI_OPT = 1 << 9
+ MTHCA_FLAG_MSI_X = 1 << 3,
+ MTHCA_FLAG_NO_LAM = 1 << 4,
+ MTHCA_FLAG_FMR = 1 << 5,
+ MTHCA_FLAG_MEMFREE = 1 << 6,
+ MTHCA_FLAG_PCIE = 1 << 7,
+ MTHCA_FLAG_SINAI_OPT = 1 << 8
};
enum {
diff --git a/trunk/drivers/infiniband/hw/mthca/mthca_eq.c b/trunk/drivers/infiniband/hw/mthca/mthca_eq.c
index b29de51b7f35..b60eb5df96e8 100644
--- a/trunk/drivers/infiniband/hw/mthca/mthca_eq.c
+++ b/trunk/drivers/infiniband/hw/mthca/mthca_eq.c
@@ -827,8 +827,7 @@ int mthca_init_eq_table(struct mthca_dev *dev)
if (err)
goto err_out_free;
- if (dev->mthca_flags & MTHCA_FLAG_MSI ||
- dev->mthca_flags & MTHCA_FLAG_MSI_X) {
+ if (dev->mthca_flags & MTHCA_FLAG_MSI_X) {
dev->eq_table.clr_mask = 0;
} else {
dev->eq_table.clr_mask =
@@ -839,8 +838,7 @@ int mthca_init_eq_table(struct mthca_dev *dev)
dev->eq_table.arm_mask = 0;
- intr = (dev->mthca_flags & MTHCA_FLAG_MSI) ?
- 128 : dev->eq_table.inta_pin;
+ intr = dev->eq_table.inta_pin;
err = mthca_create_eq(dev, dev->limits.num_cqs + MTHCA_NUM_SPARE_EQE,
(dev->mthca_flags & MTHCA_FLAG_MSI_X) ? 128 : intr,
diff --git a/trunk/drivers/infiniband/hw/mthca/mthca_main.c b/trunk/drivers/infiniband/hw/mthca/mthca_main.c
index 60de6f93869e..5cf8250d4e16 100644
--- a/trunk/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/trunk/drivers/infiniband/hw/mthca/mthca_main.c
@@ -65,14 +65,9 @@ static int msi_x = 1;
module_param(msi_x, int, 0444);
MODULE_PARM_DESC(msi_x, "attempt to use MSI-X if nonzero");
-static int msi = 0;
-module_param(msi, int, 0444);
-MODULE_PARM_DESC(msi, "attempt to use MSI if nonzero (deprecated, use MSI-X instead)");
-
#else /* CONFIG_PCI_MSI */
#define msi_x (0)
-#define msi (0)
#endif /* CONFIG_PCI_MSI */
@@ -816,13 +811,11 @@ static int mthca_setup_hca(struct mthca_dev *dev)
err = mthca_NOP(dev, &status);
if (err || status) {
- if (dev->mthca_flags & (MTHCA_FLAG_MSI | MTHCA_FLAG_MSI_X)) {
+ if (dev->mthca_flags & MTHCA_FLAG_MSI_X) {
mthca_warn(dev, "NOP command failed to generate interrupt "
"(IRQ %d).\n",
- dev->mthca_flags & MTHCA_FLAG_MSI_X ?
- dev->eq_table.eq[MTHCA_EQ_CMD].msi_x_vector :
- dev->pdev->irq);
- mthca_warn(dev, "Trying again with MSI/MSI-X disabled.\n");
+ dev->eq_table.eq[MTHCA_EQ_CMD].msi_x_vector);
+ mthca_warn(dev, "Trying again with MSI-X disabled.\n");
} else {
mthca_err(dev, "NOP command failed to generate interrupt "
"(IRQ %d), aborting.\n",
@@ -1005,7 +998,7 @@ static struct {
.flags = 0 },
[ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 8, 200),
.flags = MTHCA_FLAG_PCIE },
- [ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 2, 0),
+ [ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 3, 0),
.flags = MTHCA_FLAG_MEMFREE |
MTHCA_FLAG_PCIE },
[SINAI] = { .latest_fw = MTHCA_FW_VER(1, 2, 0),
@@ -1128,29 +1121,12 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type)
if (msi_x && !mthca_enable_msi_x(mdev))
mdev->mthca_flags |= MTHCA_FLAG_MSI_X;
- else if (msi) {
- static int warned;
-
- if (!warned) {
- printk(KERN_WARNING PFX "WARNING: MSI support will be "
- "removed from the ib_mthca driver in January 2008.\n");
- printk(KERN_WARNING " If you are using MSI and cannot "
- "switch to MSI-X, please tell "
- ".\n");
- ++warned;
- }
-
- if (!pci_enable_msi(pdev))
- mdev->mthca_flags |= MTHCA_FLAG_MSI;
- }
err = mthca_setup_hca(mdev);
- if (err == -EBUSY && (mdev->mthca_flags & (MTHCA_FLAG_MSI | MTHCA_FLAG_MSI_X))) {
+ if (err == -EBUSY && (mdev->mthca_flags & MTHCA_FLAG_MSI_X)) {
if (mdev->mthca_flags & MTHCA_FLAG_MSI_X)
pci_disable_msix(pdev);
- if (mdev->mthca_flags & MTHCA_FLAG_MSI)
- pci_disable_msi(pdev);
- mdev->mthca_flags &= ~(MTHCA_FLAG_MSI_X | MTHCA_FLAG_MSI);
+ mdev->mthca_flags &= ~MTHCA_FLAG_MSI_X;
err = mthca_setup_hca(mdev);
}
@@ -1192,8 +1168,6 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type)
err_close:
if (mdev->mthca_flags & MTHCA_FLAG_MSI_X)
pci_disable_msix(pdev);
- if (mdev->mthca_flags & MTHCA_FLAG_MSI)
- pci_disable_msi(pdev);
mthca_close_hca(mdev);
@@ -1246,8 +1220,6 @@ static void __mthca_remove_one(struct pci_dev *pdev)
if (mdev->mthca_flags & MTHCA_FLAG_MSI_X)
pci_disable_msix(pdev);
- if (mdev->mthca_flags & MTHCA_FLAG_MSI)
- pci_disable_msi(pdev);
ib_dealloc_device(&mdev->ib_dev);
mthca_release_regions(pdev, mdev->mthca_flags &
diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib.h b/trunk/drivers/infiniband/ulp/ipoib/ipoib.h
index eb7edab0e836..fe250c60607d 100644
--- a/trunk/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -56,42 +56,43 @@
/* constants */
enum {
- IPOIB_PACKET_SIZE = 2048,
- IPOIB_BUF_SIZE = IPOIB_PACKET_SIZE + IB_GRH_BYTES,
+ IPOIB_PACKET_SIZE = 2048,
+ IPOIB_BUF_SIZE = IPOIB_PACKET_SIZE + IB_GRH_BYTES,
- IPOIB_ENCAP_LEN = 4,
+ IPOIB_ENCAP_LEN = 4,
- IPOIB_CM_MTU = 0x10000 - 0x10, /* padding to align header to 16 */
- IPOIB_CM_BUF_SIZE = IPOIB_CM_MTU + IPOIB_ENCAP_LEN,
- IPOIB_CM_HEAD_SIZE = IPOIB_CM_BUF_SIZE % PAGE_SIZE,
- IPOIB_CM_RX_SG = ALIGN(IPOIB_CM_BUF_SIZE, PAGE_SIZE) / PAGE_SIZE,
- IPOIB_RX_RING_SIZE = 128,
- IPOIB_TX_RING_SIZE = 64,
+ IPOIB_CM_MTU = 0x10000 - 0x10, /* padding to align header to 16 */
+ IPOIB_CM_BUF_SIZE = IPOIB_CM_MTU + IPOIB_ENCAP_LEN,
+ IPOIB_CM_HEAD_SIZE = IPOIB_CM_BUF_SIZE % PAGE_SIZE,
+ IPOIB_CM_RX_SG = ALIGN(IPOIB_CM_BUF_SIZE, PAGE_SIZE) / PAGE_SIZE,
+ IPOIB_RX_RING_SIZE = 128,
+ IPOIB_TX_RING_SIZE = 64,
IPOIB_MAX_QUEUE_SIZE = 8192,
IPOIB_MIN_QUEUE_SIZE = 2,
+ IPOIB_CM_MAX_CONN_QP = 4096,
- IPOIB_NUM_WC = 4,
+ IPOIB_NUM_WC = 4,
IPOIB_MAX_PATH_REC_QUEUE = 3,
- IPOIB_MAX_MCAST_QUEUE = 3,
-
- IPOIB_FLAG_OPER_UP = 0,
- IPOIB_FLAG_INITIALIZED = 1,
- IPOIB_FLAG_ADMIN_UP = 2,
- IPOIB_PKEY_ASSIGNED = 3,
- IPOIB_PKEY_STOP = 4,
- IPOIB_FLAG_SUBINTERFACE = 5,
- IPOIB_MCAST_RUN = 6,
- IPOIB_STOP_REAPER = 7,
- IPOIB_MCAST_STARTED = 8,
- IPOIB_FLAG_ADMIN_CM = 9,
+ IPOIB_MAX_MCAST_QUEUE = 3,
+
+ IPOIB_FLAG_OPER_UP = 0,
+ IPOIB_FLAG_INITIALIZED = 1,
+ IPOIB_FLAG_ADMIN_UP = 2,
+ IPOIB_PKEY_ASSIGNED = 3,
+ IPOIB_PKEY_STOP = 4,
+ IPOIB_FLAG_SUBINTERFACE = 5,
+ IPOIB_MCAST_RUN = 6,
+ IPOIB_STOP_REAPER = 7,
+ IPOIB_MCAST_STARTED = 8,
+ IPOIB_FLAG_ADMIN_CM = 9,
IPOIB_FLAG_UMCAST = 10,
IPOIB_MAX_BACKOFF_SECONDS = 16,
- IPOIB_MCAST_FLAG_FOUND = 0, /* used in set_multicast_list */
+ IPOIB_MCAST_FLAG_FOUND = 0, /* used in set_multicast_list */
IPOIB_MCAST_FLAG_SENDONLY = 1,
- IPOIB_MCAST_FLAG_BUSY = 2, /* joining or already joined */
+ IPOIB_MCAST_FLAG_BUSY = 2, /* joining or already joined */
IPOIB_MCAST_FLAG_ATTACHED = 3,
};
@@ -117,7 +118,7 @@ struct ipoib_pseudoheader {
struct ipoib_mcast {
struct ib_sa_mcmember_rec mcmember;
struct ib_sa_multicast *mc;
- struct ipoib_ah *ah;
+ struct ipoib_ah *ah;
struct rb_node rb_node;
struct list_head list;
@@ -186,27 +187,29 @@ enum ipoib_cm_state {
};
struct ipoib_cm_rx {
- struct ib_cm_id *id;
- struct ib_qp *qp;
- struct list_head list;
- struct net_device *dev;
- unsigned long jiffies;
- enum ipoib_cm_state state;
+ struct ib_cm_id *id;
+ struct ib_qp *qp;
+ struct ipoib_cm_rx_buf *rx_ring;
+ struct list_head list;
+ struct net_device *dev;
+ unsigned long jiffies;
+ enum ipoib_cm_state state;
+ int recv_count;
};
struct ipoib_cm_tx {
- struct ib_cm_id *id;
- struct ib_qp *qp;
+ struct ib_cm_id *id;
+ struct ib_qp *qp;
struct list_head list;
struct net_device *dev;
struct ipoib_neigh *neigh;
struct ipoib_path *path;
struct ipoib_tx_buf *tx_ring;
- unsigned tx_head;
- unsigned tx_tail;
- unsigned long flags;
- u32 mtu;
- struct ib_wc ibwc[IPOIB_NUM_WC];
+ unsigned tx_head;
+ unsigned tx_tail;
+ unsigned long flags;
+ u32 mtu;
+ struct ib_wc ibwc[IPOIB_NUM_WC];
};
struct ipoib_cm_rx_buf {
@@ -215,25 +218,28 @@ struct ipoib_cm_rx_buf {
};
struct ipoib_cm_dev_priv {
- struct ib_srq *srq;
+ struct ib_srq *srq;
struct ipoib_cm_rx_buf *srq_ring;
- struct ib_cm_id *id;
- struct list_head passive_ids; /* state: LIVE */
- struct list_head rx_error_list; /* state: ERROR */
- struct list_head rx_flush_list; /* state: FLUSH, drain not started */
- struct list_head rx_drain_list; /* state: FLUSH, drain started */
- struct list_head rx_reap_list; /* state: FLUSH, drain done */
+ struct ib_cm_id *id;
+ struct list_head passive_ids; /* state: LIVE */
+ struct list_head rx_error_list; /* state: ERROR */
+ struct list_head rx_flush_list; /* state: FLUSH, drain not started */
+ struct list_head rx_drain_list; /* state: FLUSH, drain started */
+ struct list_head rx_reap_list; /* state: FLUSH, drain done */
struct work_struct start_task;
struct work_struct reap_task;
struct work_struct skb_task;
struct work_struct rx_reap_task;
struct delayed_work stale_task;
struct sk_buff_head skb_queue;
- struct list_head start_list;
- struct list_head reap_list;
- struct ib_wc ibwc[IPOIB_NUM_WC];
- struct ib_sge rx_sge[IPOIB_CM_RX_SG];
+ struct list_head start_list;
+ struct list_head reap_list;
+ struct ib_wc ibwc[IPOIB_NUM_WC];
+ struct ib_sge rx_sge[IPOIB_CM_RX_SG];
struct ib_recv_wr rx_wr;
+ int nonsrq_conn_qp;
+ int max_cm_mtu;
+ int num_frags;
};
/*
@@ -269,30 +275,30 @@ struct ipoib_dev_priv {
struct work_struct pkey_event_task;
struct ib_device *ca;
- u8 port;
- u16 pkey;
- u16 pkey_index;
- struct ib_pd *pd;
- struct ib_mr *mr;
- struct ib_cq *cq;
- struct ib_qp *qp;
- u32 qkey;
+ u8 port;
+ u16 pkey;
+ u16 pkey_index;
+ struct ib_pd *pd;
+ struct ib_mr *mr;
+ struct ib_cq *cq;
+ struct ib_qp *qp;
+ u32 qkey;
union ib_gid local_gid;
- u16 local_lid;
+ u16 local_lid;
unsigned int admin_mtu;
unsigned int mcast_mtu;
struct ipoib_rx_buf *rx_ring;
- spinlock_t tx_lock;
+ spinlock_t tx_lock;
struct ipoib_tx_buf *tx_ring;
- unsigned tx_head;
- unsigned tx_tail;
- struct ib_sge tx_sge;
+ unsigned tx_head;
+ unsigned tx_tail;
+ struct ib_sge tx_sge;
struct ib_send_wr tx_wr;
- unsigned tx_outstanding;
+ unsigned tx_outstanding;
struct ib_wc ibwc[IPOIB_NUM_WC];
@@ -317,10 +323,10 @@ struct ipoib_dev_priv {
struct ipoib_ah {
struct net_device *dev;
- struct ib_ah *ah;
+ struct ib_ah *ah;
struct list_head list;
- struct kref ref;
- unsigned last_send;
+ struct kref ref;
+ unsigned last_send;
};
struct ipoib_path {
@@ -331,11 +337,11 @@ struct ipoib_path {
struct list_head neigh_list;
- int query_id;
+ int query_id;
struct ib_sa_query *query;
struct completion done;
- struct rb_node rb_node;
+ struct rb_node rb_node;
struct list_head list;
};
@@ -344,7 +350,7 @@ struct ipoib_neigh {
#ifdef CONFIG_INFINIBAND_IPOIB_CM
struct ipoib_cm_tx *cm;
#endif
- union ib_gid dgid;
+ union ib_gid dgid;
struct sk_buff_head queue;
struct neighbour *neighbour;
@@ -455,12 +461,14 @@ void ipoib_drain_cq(struct net_device *dev);
#ifdef CONFIG_INFINIBAND_IPOIB_CM
-#define IPOIB_FLAGS_RC 0x80
-#define IPOIB_FLAGS_UC 0x40
+#define IPOIB_FLAGS_RC 0x80
+#define IPOIB_FLAGS_UC 0x40
/* We don't support UC connections at the moment */
#define IPOIB_CM_SUPPORTED(ha) (ha[0] & (IPOIB_FLAGS_RC))
+extern int ipoib_max_conn_qp;
+
static inline int ipoib_cm_admin_enabled(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -491,6 +499,18 @@ static inline void ipoib_cm_set(struct ipoib_neigh *neigh, struct ipoib_cm_tx *t
neigh->cm = tx;
}
+static inline int ipoib_cm_has_srq(struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ return !!priv->cm.srq;
+}
+
+static inline unsigned int ipoib_cm_max_mtu(struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ return priv->cm.max_cm_mtu;
+}
+
void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_tx *tx);
int ipoib_cm_dev_open(struct net_device *dev);
void ipoib_cm_dev_stop(struct net_device *dev);
@@ -500,7 +520,7 @@ void ipoib_cm_dev_cleanup(struct net_device *dev);
struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path *path,
struct ipoib_neigh *neigh);
void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx);
-void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb,
+void ipoib_cm_skb_too_long(struct net_device *dev, struct sk_buff *skb,
unsigned int mtu);
void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc);
void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc);
@@ -508,6 +528,8 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc);
struct ipoib_cm_tx;
+#define ipoib_max_conn_qp 0
+
static inline int ipoib_cm_admin_enabled(struct net_device *dev)
{
return 0;
@@ -533,6 +555,16 @@ static inline void ipoib_cm_set(struct ipoib_neigh *neigh, struct ipoib_cm_tx *t
{
}
+static inline int ipoib_cm_has_srq(struct net_device *dev)
+{
+ return 0;
+}
+
+static inline unsigned int ipoib_cm_max_mtu(struct net_device *dev)
+{
+ return 0;
+}
+
static inline
void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_tx *tx)
{
@@ -582,7 +614,7 @@ int ipoib_cm_add_mode_attr(struct net_device *dev)
return 0;
}
-static inline void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb,
+static inline void ipoib_cm_skb_too_long(struct net_device *dev, struct sk_buff *skb,
unsigned int mtu)
{
dev_kfree_skb_any(skb);
@@ -624,12 +656,12 @@ extern struct ib_sa_client ipoib_sa_client;
extern int ipoib_debug_level;
#define ipoib_dbg(priv, format, arg...) \
- do { \
+ do { \
if (ipoib_debug_level > 0) \
ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
} while (0)
#define ipoib_dbg_mcast(priv, format, arg...) \
- do { \
+ do { \
if (mcast_debug_level > 0) \
ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
} while (0)
@@ -642,7 +674,7 @@ extern int ipoib_debug_level;
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG_DATA
#define ipoib_dbg_data(priv, format, arg...) \
- do { \
+ do { \
if (data_debug_level > 0) \
ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
} while (0)
diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 059cf92b60a5..1818f958c250 100644
--- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -39,6 +39,15 @@
#include
#include
+#include "ipoib.h"
+
+int ipoib_max_conn_qp = 128;
+
+module_param_named(max_nonsrq_conn_qp, ipoib_max_conn_qp, int, 0444);
+MODULE_PARM_DESC(max_nonsrq_conn_qp,
+ "Max number of connected-mode QPs per interface "
+ "(applied only if shared receive queue is not available)");
+
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG_DATA
static int data_debug_level;
@@ -47,8 +56,6 @@ MODULE_PARM_DESC(cm_data_debug_level,
"Enable data path debug tracing for connected mode if > 0");
#endif
-#include "ipoib.h"
-
#define IPOIB_CM_IETF_ID 0x1000000000000000ULL
#define IPOIB_CM_RX_UPDATE_TIME (256 * HZ)
@@ -81,7 +88,7 @@ static void ipoib_cm_dma_unmap_rx(struct ipoib_dev_priv *priv, int frags,
ib_dma_unmap_single(priv->ca, mapping[i + 1], PAGE_SIZE, DMA_FROM_DEVICE);
}
-static int ipoib_cm_post_receive(struct net_device *dev, int id)
+static int ipoib_cm_post_receive_srq(struct net_device *dev, int id)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ib_recv_wr *bad_wr;
@@ -89,13 +96,13 @@ static int ipoib_cm_post_receive(struct net_device *dev, int id)
priv->cm.rx_wr.wr_id = id | IPOIB_OP_CM | IPOIB_OP_RECV;
- for (i = 0; i < IPOIB_CM_RX_SG; ++i)
+ for (i = 0; i < priv->cm.num_frags; ++i)
priv->cm.rx_sge[i].addr = priv->cm.srq_ring[id].mapping[i];
ret = ib_post_srq_recv(priv->cm.srq, &priv->cm.rx_wr, &bad_wr);
if (unlikely(ret)) {
ipoib_warn(priv, "post srq failed for buf %d (%d)\n", id, ret);
- ipoib_cm_dma_unmap_rx(priv, IPOIB_CM_RX_SG - 1,
+ ipoib_cm_dma_unmap_rx(priv, priv->cm.num_frags - 1,
priv->cm.srq_ring[id].mapping);
dev_kfree_skb_any(priv->cm.srq_ring[id].skb);
priv->cm.srq_ring[id].skb = NULL;
@@ -104,7 +111,33 @@ static int ipoib_cm_post_receive(struct net_device *dev, int id)
return ret;
}
-static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev, int id, int frags,
+static int ipoib_cm_post_receive_nonsrq(struct net_device *dev,
+ struct ipoib_cm_rx *rx, int id)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ib_recv_wr *bad_wr;
+ int i, ret;
+
+ priv->cm.rx_wr.wr_id = id | IPOIB_OP_CM | IPOIB_OP_RECV;
+
+ for (i = 0; i < IPOIB_CM_RX_SG; ++i)
+ priv->cm.rx_sge[i].addr = rx->rx_ring[id].mapping[i];
+
+ ret = ib_post_recv(rx->qp, &priv->cm.rx_wr, &bad_wr);
+ if (unlikely(ret)) {
+ ipoib_warn(priv, "post recv failed for buf %d (%d)\n", id, ret);
+ ipoib_cm_dma_unmap_rx(priv, IPOIB_CM_RX_SG - 1,
+ rx->rx_ring[id].mapping);
+ dev_kfree_skb_any(rx->rx_ring[id].skb);
+ rx->rx_ring[id].skb = NULL;
+ }
+
+ return ret;
+}
+
+static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev,
+ struct ipoib_cm_rx_buf *rx_ring,
+ int id, int frags,
u64 mapping[IPOIB_CM_RX_SG])
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -141,7 +174,7 @@ static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev, int id, int
goto partial_error;
}
- priv->cm.srq_ring[id].skb = skb;
+ rx_ring[id].skb = skb;
return skb;
partial_error:
@@ -155,7 +188,23 @@ static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev, int id, int
return NULL;
}
-static void ipoib_cm_start_rx_drain(struct ipoib_dev_priv* priv)
+static void ipoib_cm_free_rx_ring(struct net_device *dev,
+ struct ipoib_cm_rx_buf *rx_ring)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ int i;
+
+ for (i = 0; i < ipoib_recvq_size; ++i)
+ if (rx_ring[i].skb) {
+ ipoib_cm_dma_unmap_rx(priv, IPOIB_CM_RX_SG - 1,
+ rx_ring[i].mapping);
+ dev_kfree_skb_any(rx_ring[i].skb);
+ }
+
+ kfree(rx_ring);
+}
+
+static void ipoib_cm_start_rx_drain(struct ipoib_dev_priv *priv)
{
struct ib_send_wr *bad_wr;
struct ipoib_cm_rx *p;
@@ -208,12 +257,18 @@ static struct ib_qp *ipoib_cm_create_rx_qp(struct net_device *dev,
.qp_type = IB_QPT_RC,
.qp_context = p,
};
+
+ if (!ipoib_cm_has_srq(dev)) {
+ attr.cap.max_recv_wr = ipoib_recvq_size;
+ attr.cap.max_recv_sge = IPOIB_CM_RX_SG;
+ }
+
return ib_create_qp(priv->pd, &attr);
}
static int ipoib_cm_modify_rx_qp(struct net_device *dev,
- struct ib_cm_id *cm_id, struct ib_qp *qp,
- unsigned psn)
+ struct ib_cm_id *cm_id, struct ib_qp *qp,
+ unsigned psn)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ib_qp_attr qp_attr;
@@ -266,6 +321,60 @@ static int ipoib_cm_modify_rx_qp(struct net_device *dev,
return 0;
}
+static int ipoib_cm_nonsrq_init_rx(struct net_device *dev, struct ib_cm_id *cm_id,
+ struct ipoib_cm_rx *rx)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ int ret;
+ int i;
+
+ rx->rx_ring = kcalloc(ipoib_recvq_size, sizeof *rx->rx_ring, GFP_KERNEL);
+ if (!rx->rx_ring)
+ return -ENOMEM;
+
+ spin_lock_irq(&priv->lock);
+
+ if (priv->cm.nonsrq_conn_qp >= ipoib_max_conn_qp) {
+ spin_unlock_irq(&priv->lock);
+ ib_send_cm_rej(cm_id, IB_CM_REJ_NO_QP, NULL, 0, NULL, 0);
+ ret = -EINVAL;
+ goto err_free;
+ } else
+ ++priv->cm.nonsrq_conn_qp;
+
+ spin_unlock_irq(&priv->lock);
+
+ for (i = 0; i < ipoib_recvq_size; ++i) {
+ if (!ipoib_cm_alloc_rx_skb(dev, rx->rx_ring, i, IPOIB_CM_RX_SG - 1,
+ rx->rx_ring[i].mapping)) {
+ ipoib_warn(priv, "failed to allocate receive buffer %d\n", i);
+ ret = -ENOMEM;
+ goto err_count;
+ }
+ ret = ipoib_cm_post_receive_nonsrq(dev, rx, i);
+ if (ret) {
+ ipoib_warn(priv, "ipoib_cm_post_receive_nonsrq "
+ "failed for buf %d\n", i);
+ ret = -EIO;
+ goto err_count;
+ }
+ }
+
+ rx->recv_count = ipoib_recvq_size;
+
+ return 0;
+
+err_count:
+ spin_lock_irq(&priv->lock);
+ --priv->cm.nonsrq_conn_qp;
+ spin_unlock_irq(&priv->lock);
+
+err_free:
+ ipoib_cm_free_rx_ring(dev, rx->rx_ring);
+
+ return ret;
+}
+
static int ipoib_cm_send_rep(struct net_device *dev, struct ib_cm_id *cm_id,
struct ib_qp *qp, struct ib_cm_req_event_param *req,
unsigned psn)
@@ -281,7 +390,7 @@ static int ipoib_cm_send_rep(struct net_device *dev, struct ib_cm_id *cm_id,
rep.private_data_len = sizeof data;
rep.flow_control = 0;
rep.rnr_retry_count = req->rnr_retry_count;
- rep.srq = 1;
+ rep.srq = ipoib_cm_has_srq(dev);
rep.qp_num = qp->qp_num;
rep.starting_psn = psn;
return ib_send_cm_rep(cm_id, &rep);
@@ -317,6 +426,12 @@ static int ipoib_cm_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even
if (ret)
goto err_modify;
+ if (!ipoib_cm_has_srq(dev)) {
+ ret = ipoib_cm_nonsrq_init_rx(dev, cm_id, p);
+ if (ret)
+ goto err_modify;
+ }
+
spin_lock_irq(&priv->lock);
queue_delayed_work(ipoib_workqueue,
&priv->cm.stale_task, IPOIB_CM_RX_DELAY);
@@ -401,12 +516,14 @@ static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space,
void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_cm_rx_buf *rx_ring;
unsigned int wr_id = wc->wr_id & ~(IPOIB_OP_CM | IPOIB_OP_RECV);
struct sk_buff *skb, *newskb;
struct ipoib_cm_rx *p;
unsigned long flags;
u64 mapping[IPOIB_CM_RX_SG];
int frags;
+ int has_srq;
ipoib_dbg_data(priv, "cm recv completion: id %d, status: %d\n",
wr_id, wc->status);
@@ -424,18 +541,32 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
return;
}
- skb = priv->cm.srq_ring[wr_id].skb;
+ p = wc->qp->qp_context;
+
+ has_srq = ipoib_cm_has_srq(dev);
+ rx_ring = has_srq ? priv->cm.srq_ring : p->rx_ring;
+
+ skb = rx_ring[wr_id].skb;
if (unlikely(wc->status != IB_WC_SUCCESS)) {
ipoib_dbg(priv, "cm recv error "
"(status=%d, wrid=%d vend_err %x)\n",
wc->status, wr_id, wc->vendor_err);
++dev->stats.rx_dropped;
- goto repost;
+ if (has_srq)
+ goto repost;
+ else {
+ if (!--p->recv_count) {
+ spin_lock_irqsave(&priv->lock, flags);
+ list_move(&p->list, &priv->cm.rx_reap_list);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ queue_work(ipoib_workqueue, &priv->cm.rx_reap_task);
+ }
+ return;
+ }
}
if (unlikely(!(wr_id & IPOIB_CM_RX_UPDATE_MASK))) {
- p = wc->qp->qp_context;
if (p && time_after_eq(jiffies, p->jiffies + IPOIB_CM_RX_UPDATE_TIME)) {
spin_lock_irqsave(&priv->lock, flags);
p->jiffies = jiffies;
@@ -450,7 +581,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
frags = PAGE_ALIGN(wc->byte_len - min(wc->byte_len,
(unsigned)IPOIB_CM_HEAD_SIZE)) / PAGE_SIZE;
- newskb = ipoib_cm_alloc_rx_skb(dev, wr_id, frags, mapping);
+ newskb = ipoib_cm_alloc_rx_skb(dev, rx_ring, wr_id, frags, mapping);
if (unlikely(!newskb)) {
/*
* If we can't allocate a new RX buffer, dump
@@ -461,8 +592,8 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
goto repost;
}
- ipoib_cm_dma_unmap_rx(priv, frags, priv->cm.srq_ring[wr_id].mapping);
- memcpy(priv->cm.srq_ring[wr_id].mapping, mapping, (frags + 1) * sizeof *mapping);
+ ipoib_cm_dma_unmap_rx(priv, frags, rx_ring[wr_id].mapping);
+ memcpy(rx_ring[wr_id].mapping, mapping, (frags + 1) * sizeof *mapping);
ipoib_dbg_data(priv, "received %d bytes, SLID 0x%04x\n",
wc->byte_len, wc->slid);
@@ -483,9 +614,17 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
netif_receive_skb(skb);
repost:
- if (unlikely(ipoib_cm_post_receive(dev, wr_id)))
- ipoib_warn(priv, "ipoib_cm_post_receive failed "
- "for buf %d\n", wr_id);
+ if (has_srq) {
+ if (unlikely(ipoib_cm_post_receive_srq(dev, wr_id)))
+ ipoib_warn(priv, "ipoib_cm_post_receive_srq failed "
+ "for buf %d\n", wr_id);
+ } else {
+ if (unlikely(ipoib_cm_post_receive_nonsrq(dev, p, wr_id))) {
+ --p->recv_count;
+ ipoib_warn(priv, "ipoib_cm_post_receive_nonsrq failed "
+ "for buf %d\n", wr_id);
+ }
+ }
}
static inline int post_send(struct ipoib_dev_priv *priv,
@@ -495,10 +634,10 @@ static inline int post_send(struct ipoib_dev_priv *priv,
{
struct ib_send_wr *bad_wr;
- priv->tx_sge.addr = addr;
- priv->tx_sge.length = len;
+ priv->tx_sge.addr = addr;
+ priv->tx_sge.length = len;
- priv->tx_wr.wr_id = wr_id | IPOIB_OP_CM;
+ priv->tx_wr.wr_id = wr_id | IPOIB_OP_CM;
return ib_post_send(tx->qp, &priv->tx_wr, &bad_wr);
}
@@ -540,7 +679,7 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_
tx_req->mapping = addr;
if (unlikely(post_send(priv, tx, tx->tx_head & (ipoib_sendq_size - 1),
- addr, skb->len))) {
+ addr, skb->len))) {
ipoib_warn(priv, "post_send failed\n");
++dev->stats.tx_errors;
ib_dma_unmap_single(priv->ca, addr, skb->len, DMA_TO_DEVICE);
@@ -657,10 +796,33 @@ int ipoib_cm_dev_open(struct net_device *dev)
return ret;
}
+static void ipoib_cm_free_rx_reap_list(struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_cm_rx *rx, *n;
+ LIST_HEAD(list);
+
+ spin_lock_irq(&priv->lock);
+ list_splice_init(&priv->cm.rx_reap_list, &list);
+ spin_unlock_irq(&priv->lock);
+
+ list_for_each_entry_safe(rx, n, &list, list) {
+ ib_destroy_cm_id(rx->id);
+ ib_destroy_qp(rx->qp);
+ if (!ipoib_cm_has_srq(dev)) {
+ ipoib_cm_free_rx_ring(priv->dev, rx->rx_ring);
+ spin_lock_irq(&priv->lock);
+ --priv->cm.nonsrq_conn_qp;
+ spin_unlock_irq(&priv->lock);
+ }
+ kfree(rx);
+ }
+}
+
void ipoib_cm_dev_stop(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
- struct ipoib_cm_rx *p, *n;
+ struct ipoib_cm_rx *p;
unsigned long begin;
LIST_HEAD(list);
int ret;
@@ -706,15 +868,9 @@ void ipoib_cm_dev_stop(struct net_device *dev)
spin_lock_irq(&priv->lock);
}
- list_splice_init(&priv->cm.rx_reap_list, &list);
-
spin_unlock_irq(&priv->lock);
- list_for_each_entry_safe(p, n, &list, list) {
- ib_destroy_cm_id(p->id);
- ib_destroy_qp(p->qp);
- kfree(p);
- }
+ ipoib_cm_free_rx_reap_list(dev);
cancel_delayed_work(&priv->cm.stale_task);
}
@@ -799,7 +955,7 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_
.sq_sig_type = IB_SIGNAL_ALL_WR,
.qp_type = IB_QPT_RC,
.qp_context = tx
- };
+ };
return ib_create_qp(priv->pd, &attr);
}
@@ -816,28 +972,28 @@ static int ipoib_cm_send_req(struct net_device *dev,
data.qpn = cpu_to_be32(priv->qp->qp_num);
data.mtu = cpu_to_be32(IPOIB_CM_BUF_SIZE);
- req.primary_path = pathrec;
- req.alternate_path = NULL;
- req.service_id = cpu_to_be64(IPOIB_CM_IETF_ID | qpn);
- req.qp_num = qp->qp_num;
- req.qp_type = qp->qp_type;
- req.private_data = &data;
- req.private_data_len = sizeof data;
- req.flow_control = 0;
+ req.primary_path = pathrec;
+ req.alternate_path = NULL;
+ req.service_id = cpu_to_be64(IPOIB_CM_IETF_ID | qpn);
+ req.qp_num = qp->qp_num;
+ req.qp_type = qp->qp_type;
+ req.private_data = &data;
+ req.private_data_len = sizeof data;
+ req.flow_control = 0;
- req.starting_psn = 0; /* FIXME */
+ req.starting_psn = 0; /* FIXME */
/*
* Pick some arbitrary defaults here; we could make these
* module parameters if anyone cared about setting them.
*/
- req.responder_resources = 4;
- req.remote_cm_response_timeout = 20;
- req.local_cm_response_timeout = 20;
- req.retry_count = 0; /* RFC draft warns against retries */
- req.rnr_retry_count = 0; /* RFC draft warns against retries */
- req.max_cm_retries = 15;
- req.srq = 1;
+ req.responder_resources = 4;
+ req.remote_cm_response_timeout = 20;
+ req.local_cm_response_timeout = 20;
+ req.retry_count = 0; /* RFC draft warns against retries */
+ req.rnr_retry_count = 0; /* RFC draft warns against retries */
+ req.max_cm_retries = 15;
+ req.srq = ipoib_cm_has_srq(dev);
return ib_send_cm_req(id, &req);
}
@@ -1150,7 +1306,7 @@ static void ipoib_cm_skb_reap(struct work_struct *work)
spin_unlock_irq(&priv->tx_lock);
}
-void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb,
+void ipoib_cm_skb_too_long(struct net_device *dev, struct sk_buff *skb,
unsigned int mtu)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -1166,20 +1322,8 @@ void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb,
static void ipoib_cm_rx_reap(struct work_struct *work)
{
- struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv,
- cm.rx_reap_task);
- struct ipoib_cm_rx *p, *n;
- LIST_HEAD(list);
-
- spin_lock_irq(&priv->lock);
- list_splice_init(&priv->cm.rx_reap_list, &list);
- spin_unlock_irq(&priv->lock);
-
- list_for_each_entry_safe(p, n, &list, list) {
- ib_destroy_cm_id(p->id);
- ib_destroy_qp(p->qp);
- kfree(p);
- }
+ ipoib_cm_free_rx_reap_list(container_of(work, struct ipoib_dev_priv,
+ cm.rx_reap_task)->dev);
}
static void ipoib_cm_stale_task(struct work_struct *work)
@@ -1212,7 +1356,7 @@ static void ipoib_cm_stale_task(struct work_struct *work)
}
-static ssize_t show_mode(struct device *d, struct device_attribute *attr,
+static ssize_t show_mode(struct device *d, struct device_attribute *attr,
char *buf)
{
struct ipoib_dev_priv *priv = netdev_priv(to_net_dev(d));
@@ -1255,16 +1399,40 @@ int ipoib_cm_add_mode_attr(struct net_device *dev)
return device_create_file(&dev->dev, &dev_attr_mode);
}
-int ipoib_cm_dev_init(struct net_device *dev)
+static void ipoib_cm_create_srq(struct net_device *dev, int max_sge)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ib_srq_init_attr srq_init_attr = {
.attr = {
.max_wr = ipoib_recvq_size,
- .max_sge = IPOIB_CM_RX_SG
+ .max_sge = max_sge
}
};
- int ret, i;
+
+ priv->cm.srq = ib_create_srq(priv->pd, &srq_init_attr);
+ if (IS_ERR(priv->cm.srq)) {
+ if (PTR_ERR(priv->cm.srq) != -ENOSYS)
+ printk(KERN_WARNING "%s: failed to allocate SRQ, error %ld\n",
+ priv->ca->name, PTR_ERR(priv->cm.srq));
+ priv->cm.srq = NULL;
+ return;
+ }
+
+ priv->cm.srq_ring = kzalloc(ipoib_recvq_size * sizeof *priv->cm.srq_ring,
+ GFP_KERNEL);
+ if (!priv->cm.srq_ring) {
+ printk(KERN_WARNING "%s: failed to allocate CM SRQ ring (%d entries)\n",
+ priv->ca->name, ipoib_recvq_size);
+ ib_destroy_srq(priv->cm.srq);
+ priv->cm.srq = NULL;
+ }
+}
+
+int ipoib_cm_dev_init(struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ int i, ret;
+ struct ib_device_attr attr;
INIT_LIST_HEAD(&priv->cm.passive_ids);
INIT_LIST_HEAD(&priv->cm.reap_list);
@@ -1281,43 +1449,53 @@ int ipoib_cm_dev_init(struct net_device *dev)
skb_queue_head_init(&priv->cm.skb_queue);
- priv->cm.srq = ib_create_srq(priv->pd, &srq_init_attr);
- if (IS_ERR(priv->cm.srq)) {
- ret = PTR_ERR(priv->cm.srq);
- priv->cm.srq = NULL;
+ ret = ib_query_device(priv->ca, &attr);
+ if (ret) {
+ printk(KERN_WARNING "ib_query_device() failed with %d\n", ret);
return ret;
}
- priv->cm.srq_ring = kzalloc(ipoib_recvq_size * sizeof *priv->cm.srq_ring,
- GFP_KERNEL);
- if (!priv->cm.srq_ring) {
- printk(KERN_WARNING "%s: failed to allocate CM ring (%d entries)\n",
- priv->ca->name, ipoib_recvq_size);
- ipoib_cm_dev_cleanup(dev);
- return -ENOMEM;
+ ipoib_dbg(priv, "max_srq_sge=%d\n", attr.max_srq_sge);
+
+ attr.max_srq_sge = min_t(int, IPOIB_CM_RX_SG, attr.max_srq_sge);
+ ipoib_cm_create_srq(dev, attr.max_srq_sge);
+ if (ipoib_cm_has_srq(dev)) {
+ priv->cm.max_cm_mtu = attr.max_srq_sge * PAGE_SIZE - 0x10;
+ priv->cm.num_frags = attr.max_srq_sge;
+ ipoib_dbg(priv, "max_cm_mtu = 0x%x, num_frags=%d\n",
+ priv->cm.max_cm_mtu, priv->cm.num_frags);
+ } else {
+ priv->cm.max_cm_mtu = IPOIB_CM_MTU;
+ priv->cm.num_frags = IPOIB_CM_RX_SG;
}
- for (i = 0; i < IPOIB_CM_RX_SG; ++i)
+ for (i = 0; i < priv->cm.num_frags; ++i)
priv->cm.rx_sge[i].lkey = priv->mr->lkey;
priv->cm.rx_sge[0].length = IPOIB_CM_HEAD_SIZE;
- for (i = 1; i < IPOIB_CM_RX_SG; ++i)
+ for (i = 1; i < priv->cm.num_frags; ++i)
priv->cm.rx_sge[i].length = PAGE_SIZE;
priv->cm.rx_wr.next = NULL;
priv->cm.rx_wr.sg_list = priv->cm.rx_sge;
- priv->cm.rx_wr.num_sge = IPOIB_CM_RX_SG;
+ priv->cm.rx_wr.num_sge = priv->cm.num_frags;
+
+ if (ipoib_cm_has_srq(dev)) {
+ for (i = 0; i < ipoib_recvq_size; ++i) {
+ if (!ipoib_cm_alloc_rx_skb(dev, priv->cm.srq_ring, i,
+ priv->cm.num_frags - 1,
+ priv->cm.srq_ring[i].mapping)) {
+ ipoib_warn(priv, "failed to allocate "
+ "receive buffer %d\n", i);
+ ipoib_cm_dev_cleanup(dev);
+ return -ENOMEM;
+ }
- for (i = 0; i < ipoib_recvq_size; ++i) {
- if (!ipoib_cm_alloc_rx_skb(dev, i, IPOIB_CM_RX_SG - 1,
- priv->cm.srq_ring[i].mapping)) {
- ipoib_warn(priv, "failed to allocate receive buffer %d\n", i);
- ipoib_cm_dev_cleanup(dev);
- return -ENOMEM;
- }
- if (ipoib_cm_post_receive(dev, i)) {
- ipoib_warn(priv, "ipoib_ib_post_receive failed for buf %d\n", i);
- ipoib_cm_dev_cleanup(dev);
- return -EIO;
+ if (ipoib_cm_post_receive_srq(dev, i)) {
+ ipoib_warn(priv, "ipoib_cm_post_receive_srq "
+ "failed for buf %d\n", i);
+ ipoib_cm_dev_cleanup(dev);
+ return -EIO;
+ }
}
}
@@ -1328,7 +1506,7 @@ int ipoib_cm_dev_init(struct net_device *dev)
void ipoib_cm_dev_cleanup(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
- int i, ret;
+ int ret;
if (!priv->cm.srq)
return;
@@ -1342,13 +1520,7 @@ void ipoib_cm_dev_cleanup(struct net_device *dev)
priv->cm.srq = NULL;
if (!priv->cm.srq_ring)
return;
- for (i = 0; i < ipoib_recvq_size; ++i)
- if (priv->cm.srq_ring[i].skb) {
- ipoib_cm_dma_unmap_rx(priv, IPOIB_CM_RX_SG - 1,
- priv->cm.srq_ring[i].mapping);
- dev_kfree_skb_any(priv->cm.srq_ring[i].skb);
- priv->cm.srq_ring[i].skb = NULL;
- }
- kfree(priv->cm.srq_ring);
+
+ ipoib_cm_free_rx_ring(dev, priv->cm.srq_ring);
priv->cm.srq_ring = NULL;
}
diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_fs.c
index 44c174182a82..8b882bbd1d05 100644
--- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_fs.c
+++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_fs.c
@@ -124,7 +124,7 @@ static int ipoib_mcg_seq_show(struct seq_file *file, void *iter_ptr)
return 0;
}
-static struct seq_operations ipoib_mcg_seq_ops = {
+static const struct seq_operations ipoib_mcg_seq_ops = {
.start = ipoib_mcg_seq_start,
.next = ipoib_mcg_seq_next,
.stop = ipoib_mcg_seq_stop,
@@ -230,7 +230,7 @@ static int ipoib_path_seq_show(struct seq_file *file, void *iter_ptr)
return 0;
}
-static struct seq_operations ipoib_path_seq_ops = {
+static const struct seq_operations ipoib_path_seq_ops = {
.start = ipoib_path_seq_start,
.next = ipoib_path_seq_next,
.stop = ipoib_path_seq_stop,
diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index 5063dd509ad2..52bc2bd5799a 100644
--- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -345,12 +345,12 @@ static inline int post_send(struct ipoib_dev_priv *priv,
{
struct ib_send_wr *bad_wr;
- priv->tx_sge.addr = addr;
- priv->tx_sge.length = len;
+ priv->tx_sge.addr = addr;
+ priv->tx_sge.length = len;
- priv->tx_wr.wr_id = wr_id;
+ priv->tx_wr.wr_id = wr_id;
priv->tx_wr.wr.ud.remote_qpn = qpn;
- priv->tx_wr.wr.ud.ah = address;
+ priv->tx_wr.wr.ud.ah = address;
return ib_post_send(priv->qp, &priv->tx_wr, &bad_wr);
}
diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c
index c9f6077b615e..a082466f4a83 100644
--- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -182,17 +182,20 @@ static int ipoib_change_mtu(struct net_device *dev, int new_mtu)
struct ipoib_dev_priv *priv = netdev_priv(dev);
/* dev->mtu > 2K ==> connected mode */
- if (ipoib_cm_admin_enabled(dev) && new_mtu <= IPOIB_CM_MTU) {
+ if (ipoib_cm_admin_enabled(dev)) {
+ if (new_mtu > ipoib_cm_max_mtu(dev))
+ return -EINVAL;
+
if (new_mtu > priv->mcast_mtu)
ipoib_warn(priv, "mtu > %d will cause multicast packet drops.\n",
priv->mcast_mtu);
+
dev->mtu = new_mtu;
return 0;
}
- if (new_mtu > IPOIB_PACKET_SIZE - IPOIB_ENCAP_LEN) {
+ if (new_mtu > IPOIB_PACKET_SIZE - IPOIB_ENCAP_LEN)
return -EINVAL;
- }
priv->admin_mtu = new_mtu;
@@ -474,8 +477,8 @@ static struct ipoib_path *path_rec_create(struct net_device *dev, void *gid)
INIT_LIST_HEAD(&path->neigh_list);
memcpy(path->pathrec.dgid.raw, gid, sizeof (union ib_gid));
- path->pathrec.sgid = priv->local_gid;
- path->pathrec.pkey = cpu_to_be16(priv->pkey);
+ path->pathrec.sgid = priv->local_gid;
+ path->pathrec.pkey = cpu_to_be16(priv->pkey);
path->pathrec.numb_path = 1;
path->pathrec.traffic_class = priv->broadcast->mcmember.traffic_class;
@@ -669,16 +672,6 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(!spin_trylock_irqsave(&priv->tx_lock, flags)))
return NETDEV_TX_LOCKED;
- /*
- * Check if our queue is stopped. Since we have the LLTX bit
- * set, we can't rely on netif_stop_queue() preventing our
- * xmit function from being called with a full queue.
- */
- if (unlikely(netif_queue_stopped(dev))) {
- spin_unlock_irqrestore(&priv->tx_lock, flags);
- return NETDEV_TX_BUSY;
- }
-
if (likely(skb->dst && skb->dst->neighbour)) {
if (unlikely(!*to_ipoib_neigh(skb->dst->neighbour))) {
ipoib_path_lookup(skb, dev);
@@ -950,34 +943,34 @@ static void ipoib_setup(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
- dev->open = ipoib_open;
- dev->stop = ipoib_stop;
- dev->change_mtu = ipoib_change_mtu;
- dev->hard_start_xmit = ipoib_start_xmit;
- dev->tx_timeout = ipoib_timeout;
- dev->header_ops = &ipoib_header_ops;
- dev->set_multicast_list = ipoib_set_mcast_list;
- dev->neigh_setup = ipoib_neigh_setup_dev;
+ dev->open = ipoib_open;
+ dev->stop = ipoib_stop;
+ dev->change_mtu = ipoib_change_mtu;
+ dev->hard_start_xmit = ipoib_start_xmit;
+ dev->tx_timeout = ipoib_timeout;
+ dev->header_ops = &ipoib_header_ops;
+ dev->set_multicast_list = ipoib_set_mcast_list;
+ dev->neigh_setup = ipoib_neigh_setup_dev;
netif_napi_add(dev, &priv->napi, ipoib_poll, 100);
- dev->watchdog_timeo = HZ;
+ dev->watchdog_timeo = HZ;
- dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+ dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
/*
* We add in INFINIBAND_ALEN to allow for the destination
* address "pseudoheader" for skbs without neighbour struct.
*/
- dev->hard_header_len = IPOIB_ENCAP_LEN + INFINIBAND_ALEN;
- dev->addr_len = INFINIBAND_ALEN;
- dev->type = ARPHRD_INFINIBAND;
- dev->tx_queue_len = ipoib_sendq_size * 2;
- dev->features = NETIF_F_VLAN_CHALLENGED | NETIF_F_LLTX;
+ dev->hard_header_len = IPOIB_ENCAP_LEN + INFINIBAND_ALEN;
+ dev->addr_len = INFINIBAND_ALEN;
+ dev->type = ARPHRD_INFINIBAND;
+ dev->tx_queue_len = ipoib_sendq_size * 2;
+ dev->features = NETIF_F_VLAN_CHALLENGED | NETIF_F_LLTX;
/* MTU will be reset when mcast join happens */
- dev->mtu = IPOIB_PACKET_SIZE - IPOIB_ENCAP_LEN;
- priv->mcast_mtu = priv->admin_mtu = dev->mtu;
+ dev->mtu = IPOIB_PACKET_SIZE - IPOIB_ENCAP_LEN;
+ priv->mcast_mtu = priv->admin_mtu = dev->mtu;
memcpy(dev->broadcast, ipv4_bcast_addr, INFINIBAND_ALEN);
@@ -1268,6 +1261,9 @@ static int __init ipoib_init_module(void)
ipoib_sendq_size = roundup_pow_of_two(ipoib_sendq_size);
ipoib_sendq_size = min(ipoib_sendq_size, IPOIB_MAX_QUEUE_SIZE);
ipoib_sendq_size = max(ipoib_sendq_size, IPOIB_MIN_QUEUE_SIZE);
+#ifdef CONFIG_INFINIBAND_IPOIB_CM
+ ipoib_max_conn_qp = min(ipoib_max_conn_qp, IPOIB_CM_MAX_CONN_QP);
+#endif
ret = ipoib_register_debugfs();
if (ret)
diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 9bcfc7ad6aa6..2628339e3a99 100644
--- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -702,7 +702,7 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
out:
if (mcast && mcast->ah) {
- if (skb->dst &&
+ if (skb->dst &&
skb->dst->neighbour &&
!*to_ipoib_neigh(skb->dst->neighbour)) {
struct ipoib_neigh *neigh = ipoib_neigh_alloc(skb->dst->neighbour,
@@ -710,7 +710,7 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
if (neigh) {
kref_get(&mcast->ah->ref);
- neigh->ah = mcast->ah;
+ neigh->ah = mcast->ah;
list_add_tail(&neigh->list, &mcast->neigh_list);
}
}
@@ -788,10 +788,6 @@ void ipoib_mcast_restart_task(struct work_struct *work)
memcpy(mgid.raw, mclist->dmi_addr + 4, sizeof mgid);
- /* Add in the P_Key */
- mgid.raw[4] = (priv->pkey >> 8) & 0xff;
- mgid.raw[5] = priv->pkey & 0xff;
-
mcast = __ipoib_mcast_find(dev, &mgid);
if (!mcast || test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
struct ipoib_mcast *nmcast;
diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
index 3c6e45db0ab5..433e99ac227b 100644
--- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
+++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
@@ -172,8 +172,12 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
size = ipoib_sendq_size + ipoib_recvq_size + 1;
ret = ipoib_cm_dev_init(dev);
- if (!ret)
- size += ipoib_recvq_size + 1 /* 1 extra for rx_drain_qp */;
+ if (!ret) {
+ if (ipoib_cm_has_srq(dev))
+ size += ipoib_recvq_size + 1; /* 1 extra for rx_drain_qp */
+ else
+ size += ipoib_recvq_size * ipoib_max_conn_qp;
+ }
priv->cq = ib_create_cq(priv->ca, ipoib_ib_completion, NULL, dev, size, 0);
if (IS_ERR(priv->cq)) {
@@ -197,12 +201,12 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
priv->dev->dev_addr[2] = (priv->qp->qp_num >> 8) & 0xff;
priv->dev->dev_addr[3] = (priv->qp->qp_num ) & 0xff;
- priv->tx_sge.lkey = priv->mr->lkey;
+ priv->tx_sge.lkey = priv->mr->lkey;
- priv->tx_wr.opcode = IB_WR_SEND;
- priv->tx_wr.sg_list = &priv->tx_sge;
- priv->tx_wr.num_sge = 1;
- priv->tx_wr.send_flags = IB_SEND_SIGNALED;
+ priv->tx_wr.opcode = IB_WR_SEND;
+ priv->tx_wr.sg_list = &priv->tx_sge;
+ priv->tx_wr.num_sge = 1;
+ priv->tx_wr.send_flags = IB_SEND_SIGNALED;
return 0;
diff --git a/trunk/drivers/infiniband/ulp/iser/Kconfig b/trunk/drivers/infiniband/ulp/iser/Kconfig
index fe604c8d2996..77dedba829e6 100644
--- a/trunk/drivers/infiniband/ulp/iser/Kconfig
+++ b/trunk/drivers/infiniband/ulp/iser/Kconfig
@@ -8,5 +8,5 @@ config INFINIBAND_ISER
that speak iSCSI over iSER over InfiniBand.
The iSER protocol is defined by IETF.
- See
- and
+ See
+ and
diff --git a/trunk/drivers/infiniband/ulp/iser/iscsi_iser.c b/trunk/drivers/infiniband/ulp/iser/iscsi_iser.c
index bad8dacafd10..dfa5a4544187 100644
--- a/trunk/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/trunk/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -551,6 +551,7 @@ static struct scsi_host_template iscsi_iser_sht = {
.module = THIS_MODULE,
.name = "iSCSI Initiator over iSER, v." DRV_VER,
.queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
.can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
.sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
.max_sectors = 1024,
diff --git a/trunk/drivers/infiniband/ulp/iser/iser_initiator.c b/trunk/drivers/infiniband/ulp/iser/iser_initiator.c
index a6f2303ed14a..ba1b455949c0 100644
--- a/trunk/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/trunk/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -561,7 +561,7 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
if (opcode == ISCSI_OP_SCSI_CMD_RSP) {
itt = get_itt(hdr->itt); /* mask out cid and age bits */
if (!(itt < session->cmds_max))
- iser_err("itt can't be matched to task!!!"
+ iser_err("itt can't be matched to task!!! "
"conn %p opcode %d cmds_max %d itt %d\n",
conn->iscsi_conn,opcode,session->cmds_max,itt);
/* use the mapping given with the cmds array indexed by itt */
diff --git a/trunk/drivers/infiniband/ulp/iser/iser_verbs.c b/trunk/drivers/infiniband/ulp/iser/iser_verbs.c
index 654a4dce0236..714b8db02b29 100644
--- a/trunk/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/trunk/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -105,7 +105,7 @@ static int iser_create_device_ib_res(struct iser_device *device)
}
/**
- * iser_free_device_ib_res - destory/dealloc/dereg the DMA MR,
+ * iser_free_device_ib_res - destroy/dealloc/dereg the DMA MR,
* CQ and PD created with the device associated with the adapator.
*/
static void iser_free_device_ib_res(struct iser_device *device)
@@ -475,13 +475,11 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
iser_disconnected_handler(cma_id);
break;
case RDMA_CM_EVENT_DEVICE_REMOVAL:
+ iser_err("Device removal is currently unsupported\n");
BUG();
break;
- case RDMA_CM_EVENT_CONNECT_RESPONSE:
- BUG();
- break;
- case RDMA_CM_EVENT_CONNECT_REQUEST:
default:
+ iser_err("Unexpected RDMA CM event (%d)\n", event->event);
break;
}
return ret;
diff --git a/trunk/drivers/infiniband/ulp/srp/ib_srp.c b/trunk/drivers/infiniband/ulp/srp/ib_srp.c
index bdb6f8517401..f2d2c7e2c76b 100644
--- a/trunk/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/trunk/drivers/infiniband/ulp/srp/ib_srp.c
@@ -272,7 +272,8 @@ static void srp_path_rec_completion(int status,
target->status = status;
if (status)
- printk(KERN_ERR PFX "Got failed path rec status %d\n", status);
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "Got failed path rec status %d\n", status);
else
target->path = *pathrec;
complete(&target->done);
@@ -303,7 +304,8 @@ static int srp_lookup_path(struct srp_target_port *target)
wait_for_completion(&target->done);
if (target->status < 0)
- printk(KERN_WARNING PFX "Path record query failed\n");
+ shost_printk(KERN_WARNING, target->scsi_host,
+ PFX "Path record query failed\n");
return target->status;
}
@@ -379,9 +381,10 @@ static int srp_send_req(struct srp_target_port *target)
* the second 8 bytes to the local node GUID.
*/
if (srp_target_is_topspin(target)) {
- printk(KERN_DEBUG PFX "Topspin/Cisco initiator port ID workaround "
- "activated for target GUID %016llx\n",
- (unsigned long long) be64_to_cpu(target->ioc_guid));
+ shost_printk(KERN_DEBUG, target->scsi_host,
+ PFX "Topspin/Cisco initiator port ID workaround "
+ "activated for target GUID %016llx\n",
+ (unsigned long long) be64_to_cpu(target->ioc_guid));
memset(req->priv.initiator_port_id, 0, 8);
memcpy(req->priv.initiator_port_id + 8,
&target->srp_host->dev->dev->node_guid, 8);
@@ -400,7 +403,8 @@ static void srp_disconnect_target(struct srp_target_port *target)
init_completion(&target->done);
if (ib_send_cm_dreq(target->cm_id, NULL, 0)) {
- printk(KERN_DEBUG PFX "Sending CM DREQ failed\n");
+ shost_printk(KERN_DEBUG, target->scsi_host,
+ PFX "Sending CM DREQ failed\n");
return;
}
wait_for_completion(&target->done);
@@ -568,7 +572,8 @@ static int srp_reconnect_target(struct srp_target_port *target)
return ret;
err:
- printk(KERN_ERR PFX "reconnect failed (%d), removing target port.\n", ret);
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "reconnect failed (%d), removing target port.\n", ret);
/*
* We couldn't reconnect, so kill our target port off.
@@ -683,8 +688,9 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
if (scmnd->sc_data_direction != DMA_FROM_DEVICE &&
scmnd->sc_data_direction != DMA_TO_DEVICE) {
- printk(KERN_WARNING PFX "Unhandled data direction %d\n",
- scmnd->sc_data_direction);
+ shost_printk(KERN_WARNING, target->scsi_host,
+ PFX "Unhandled data direction %d\n",
+ scmnd->sc_data_direction);
return -EINVAL;
}
@@ -786,8 +792,9 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
} else {
scmnd = req->scmnd;
if (!scmnd)
- printk(KERN_ERR "Null scmnd for RSP w/tag %016llx\n",
- (unsigned long long) rsp->tag);
+ shost_printk(KERN_ERR, target->scsi_host,
+ "Null scmnd for RSP w/tag %016llx\n",
+ (unsigned long long) rsp->tag);
scmnd->result = rsp->status;
if (rsp->flags & SRP_RSP_FLAG_SNSVALID) {
@@ -831,7 +838,8 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
if (0) {
int i;
- printk(KERN_ERR PFX "recv completion, opcode 0x%02x\n", opcode);
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "recv completion, opcode 0x%02x\n", opcode);
for (i = 0; i < wc->byte_len; ++i) {
if (i % 8 == 0)
@@ -852,11 +860,13 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
case SRP_T_LOGOUT:
/* XXX Handle target logout */
- printk(KERN_WARNING PFX "Got target logout request\n");
+ shost_printk(KERN_WARNING, target->scsi_host,
+ PFX "Got target logout request\n");
break;
default:
- printk(KERN_WARNING PFX "Unhandled SRP opcode 0x%02x\n", opcode);
+ shost_printk(KERN_WARNING, target->scsi_host,
+ PFX "Unhandled SRP opcode 0x%02x\n", opcode);
break;
}
@@ -872,9 +882,10 @@ static void srp_completion(struct ib_cq *cq, void *target_ptr)
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
while (ib_poll_cq(cq, 1, &wc) > 0) {
if (wc.status) {
- printk(KERN_ERR PFX "failed %s status %d\n",
- wc.wr_id & SRP_OP_RECV ? "receive" : "send",
- wc.status);
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "failed %s status %d\n",
+ wc.wr_id & SRP_OP_RECV ? "receive" : "send",
+ wc.status);
target->qp_in_error = 1;
break;
}
@@ -930,13 +941,18 @@ static int srp_post_recv(struct srp_target_port *target)
* req_lim and tx_head. Lock cannot be dropped between call here and
* call to __srp_post_send().
*/
-static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target)
+static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target,
+ enum srp_request_type req_type)
{
+ s32 min = (req_type == SRP_REQ_TASK_MGMT) ? 1 : 2;
+
if (target->tx_head - target->tx_tail >= SRP_SQ_SIZE)
return NULL;
- if (unlikely(target->req_lim < 1))
+ if (target->req_lim < min) {
++target->zero_req_lim;
+ return NULL;
+ }
return target->tx_ring[target->tx_head & SRP_SQ_SIZE];
}
@@ -993,7 +1009,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
return 0;
}
- iu = __srp_get_tx_iu(target);
+ iu = __srp_get_tx_iu(target, SRP_REQ_NORMAL);
if (!iu)
goto err;
@@ -1022,12 +1038,13 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
len = srp_map_data(scmnd, target, req);
if (len < 0) {
- printk(KERN_ERR PFX "Failed to map data\n");
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "Failed to map data\n");
goto err;
}
if (__srp_post_recv(target)) {
- printk(KERN_ERR PFX "Recv failed\n");
+ shost_printk(KERN_ERR, target->scsi_host, PFX "Recv failed\n");
goto err_unmap;
}
@@ -1035,7 +1052,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
DMA_TO_DEVICE);
if (__srp_post_send(target, iu, len)) {
- printk(KERN_ERR PFX "Send failed\n");
+ shost_printk(KERN_ERR, target->scsi_host, PFX "Send failed\n");
goto err_unmap;
}
@@ -1090,6 +1107,7 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id,
struct ib_cm_event *event,
struct srp_target_port *target)
{
+ struct Scsi_Host *shost = target->scsi_host;
struct ib_class_port_info *cpi;
int opcode;
@@ -1115,19 +1133,22 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id,
memcpy(target->path.dgid.raw,
event->param.rej_rcvd.ari, 16);
- printk(KERN_DEBUG PFX "Topspin/Cisco redirect to target port GID %016llx%016llx\n",
- (unsigned long long) be64_to_cpu(target->path.dgid.global.subnet_prefix),
- (unsigned long long) be64_to_cpu(target->path.dgid.global.interface_id));
+ shost_printk(KERN_DEBUG, shost,
+ PFX "Topspin/Cisco redirect to target port GID %016llx%016llx\n",
+ (unsigned long long) be64_to_cpu(target->path.dgid.global.subnet_prefix),
+ (unsigned long long) be64_to_cpu(target->path.dgid.global.interface_id));
target->status = SRP_PORT_REDIRECT;
} else {
- printk(KERN_WARNING " REJ reason: IB_CM_REJ_PORT_REDIRECT\n");
+ shost_printk(KERN_WARNING, shost,
+ " REJ reason: IB_CM_REJ_PORT_REDIRECT\n");
target->status = -ECONNRESET;
}
break;
case IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID:
- printk(KERN_WARNING " REJ reason: IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID\n");
+ shost_printk(KERN_WARNING, shost,
+ " REJ reason: IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID\n");
target->status = -ECONNRESET;
break;
@@ -1138,20 +1159,21 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id,
u32 reason = be32_to_cpu(rej->reason);
if (reason == SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE)
- printk(KERN_WARNING PFX
- "SRP_LOGIN_REJ: requested max_it_iu_len too large\n");
+ shost_printk(KERN_WARNING, shost,
+ PFX "SRP_LOGIN_REJ: requested max_it_iu_len too large\n");
else
- printk(KERN_WARNING PFX
- "SRP LOGIN REJECTED, reason 0x%08x\n", reason);
+ shost_printk(KERN_WARNING, shost,
+ PFX "SRP LOGIN REJECTED, reason 0x%08x\n", reason);
} else
- printk(KERN_WARNING " REJ reason: IB_CM_REJ_CONSUMER_DEFINED,"
- " opcode 0x%02x\n", opcode);
+ shost_printk(KERN_WARNING, shost,
+ " REJ reason: IB_CM_REJ_CONSUMER_DEFINED,"
+ " opcode 0x%02x\n", opcode);
target->status = -ECONNRESET;
break;
default:
- printk(KERN_WARNING " REJ reason 0x%x\n",
- event->param.rej_rcvd.reason);
+ shost_printk(KERN_WARNING, shost, " REJ reason 0x%x\n",
+ event->param.rej_rcvd.reason);
target->status = -ECONNRESET;
}
}
@@ -1166,7 +1188,8 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
switch (event->event) {
case IB_CM_REQ_ERROR:
- printk(KERN_DEBUG PFX "Sending CM REQ failed\n");
+ shost_printk(KERN_DEBUG, target->scsi_host,
+ PFX "Sending CM REQ failed\n");
comp = 1;
target->status = -ECONNRESET;
break;
@@ -1184,7 +1207,8 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
target->scsi_host->can_queue = min(target->req_lim,
target->scsi_host->can_queue);
} else {
- printk(KERN_WARNING PFX "Unhandled RSP opcode %#x\n", opcode);
+ shost_printk(KERN_WARNING, target->scsi_host,
+ PFX "Unhandled RSP opcode %#x\n", opcode);
target->status = -ECONNRESET;
break;
}
@@ -1230,20 +1254,23 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
break;
case IB_CM_REJ_RECEIVED:
- printk(KERN_DEBUG PFX "REJ received\n");
+ shost_printk(KERN_DEBUG, target->scsi_host, PFX "REJ received\n");
comp = 1;
srp_cm_rej_handler(cm_id, event, target);
break;
case IB_CM_DREQ_RECEIVED:
- printk(KERN_WARNING PFX "DREQ received - connection closed\n");
+ shost_printk(KERN_WARNING, target->scsi_host,
+ PFX "DREQ received - connection closed\n");
if (ib_send_cm_drep(cm_id, NULL, 0))
- printk(KERN_ERR PFX "Sending CM DREP failed\n");
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "Sending CM DREP failed\n");
break;
case IB_CM_TIMEWAIT_EXIT:
- printk(KERN_ERR PFX "connection closed\n");
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "connection closed\n");
comp = 1;
target->status = 0;
@@ -1255,7 +1282,8 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
break;
default:
- printk(KERN_WARNING PFX "Unhandled CM event %d\n", event->event);
+ shost_printk(KERN_WARNING, target->scsi_host,
+ PFX "Unhandled CM event %d\n", event->event);
break;
}
@@ -1283,7 +1311,7 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
init_completion(&req->done);
- iu = __srp_get_tx_iu(target);
+ iu = __srp_get_tx_iu(target, SRP_REQ_TASK_MGMT);
if (!iu)
goto out;
@@ -1332,7 +1360,7 @@ static int srp_abort(struct scsi_cmnd *scmnd)
struct srp_request *req;
int ret = SUCCESS;
- printk(KERN_ERR "SRP abort called\n");
+ shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
if (target->qp_in_error)
return FAILED;
@@ -1362,7 +1390,7 @@ static int srp_reset_device(struct scsi_cmnd *scmnd)
struct srp_target_port *target = host_to_target(scmnd->device->host);
struct srp_request *req, *tmp;
- printk(KERN_ERR "SRP reset_device called\n");
+ shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n");
if (target->qp_in_error)
return FAILED;
@@ -1389,7 +1417,7 @@ static int srp_reset_host(struct scsi_cmnd *scmnd)
struct srp_target_port *target = host_to_target(scmnd->device->host);
int ret = FAILED;
- printk(KERN_ERR PFX "SRP reset_host called\n");
+ shost_printk(KERN_ERR, target->scsi_host, PFX "SRP reset_host called\n");
if (!srp_reconnect_target(target))
ret = SUCCESS;
@@ -1543,6 +1571,7 @@ static struct scsi_host_template srp_template = {
.this_id = -1,
.cmd_per_lun = SRP_SQ_SIZE,
.use_clustering = ENABLE_CLUSTERING,
+ .use_sg_chaining = ENABLE_SG_CHAINING,
.shost_attrs = srp_host_attrs
};
@@ -1814,8 +1843,9 @@ static ssize_t srp_create_target(struct class_device *class_dev,
ib_get_cached_gid(host->dev->dev, host->port, 0, &target->path.sgid);
- printk(KERN_DEBUG PFX "new target: id_ext %016llx ioc_guid %016llx pkey %04x "
- "service_id %016llx dgid %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ shost_printk(KERN_DEBUG, target->scsi_host, PFX
+ "new target: id_ext %016llx ioc_guid %016llx pkey %04x "
+ "service_id %016llx dgid %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
(unsigned long long) be64_to_cpu(target->id_ext),
(unsigned long long) be64_to_cpu(target->ioc_guid),
be16_to_cpu(target->path.pkey),
@@ -1842,7 +1872,8 @@ static ssize_t srp_create_target(struct class_device *class_dev,
target->qp_in_error = 0;
ret = srp_connect_target(target);
if (ret) {
- printk(KERN_ERR PFX "Connection failed\n");
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "Connection failed\n");
goto err_cm_id;
}
diff --git a/trunk/drivers/infiniband/ulp/srp/ib_srp.h b/trunk/drivers/infiniband/ulp/srp/ib_srp.h
index e3573e7038c4..4a3c1f37e4c2 100644
--- a/trunk/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/trunk/drivers/infiniband/ulp/srp/ib_srp.h
@@ -79,6 +79,11 @@ enum srp_target_state {
SRP_TARGET_REMOVED
};
+enum srp_request_type {
+ SRP_REQ_NORMAL,
+ SRP_REQ_TASK_MGMT,
+};
+
struct srp_device {
struct list_head dev_list;
struct ib_device *dev;
diff --git a/trunk/drivers/lguest/x86/core.c b/trunk/drivers/lguest/x86/core.c
index 482aec2a9631..96d0fd07c57d 100644
--- a/trunk/drivers/lguest/x86/core.c
+++ b/trunk/drivers/lguest/x86/core.c
@@ -459,7 +459,7 @@ void __init lguest_arch_host_init(void)
/* We don't need the complexity of CPUs coming and going while we're
* doing this. */
- lock_cpu_hotplug();
+ get_online_cpus();
if (cpu_has_pge) { /* We have a broader idea of "global". */
/* Remember that this was originally set (for cleanup). */
cpu_had_pge = 1;
@@ -469,20 +469,20 @@ void __init lguest_arch_host_init(void)
/* Turn off the feature in the global feature set. */
clear_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability);
}
- unlock_cpu_hotplug();
+ put_online_cpus();
};
/*:*/
void __exit lguest_arch_host_fini(void)
{
/* If we had PGE before we started, turn it back on now. */
- lock_cpu_hotplug();
+ get_online_cpus();
if (cpu_had_pge) {
set_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability);
/* adjust_pge's argument "1" means set PGE. */
on_each_cpu(adjust_pge, (void *)1, 0, 1);
}
- unlock_cpu_hotplug();
+ put_online_cpus();
}
diff --git a/trunk/drivers/media/Kconfig b/trunk/drivers/media/Kconfig
index 1604f0490404..8f4a45346de7 100644
--- a/trunk/drivers/media/Kconfig
+++ b/trunk/drivers/media/Kconfig
@@ -69,11 +69,13 @@ source "drivers/media/common/Kconfig"
config VIDEO_TUNER
tristate
depends on I2C
+ select TUNER_XC2028 if !VIDEO_TUNER_CUSTOMIZE
select TUNER_MT20XX if !VIDEO_TUNER_CUSTOMIZE
select TUNER_TDA8290 if !VIDEO_TUNER_CUSTOMIZE
select TUNER_TEA5761 if !VIDEO_TUNER_CUSTOMIZE
select TUNER_TEA5767 if !VIDEO_TUNER_CUSTOMIZE
select TUNER_SIMPLE if !VIDEO_TUNER_CUSTOMIZE
+ select TUNER_TDA9887 if !VIDEO_TUNER_CUSTOMIZE
menuconfig VIDEO_TUNER_CUSTOMIZE
bool "Customize analog tuner modules to build"
@@ -89,6 +91,13 @@ menuconfig VIDEO_TUNER_CUSTOMIZE
if VIDEO_TUNER_CUSTOMIZE
+config TUNER_XC2028
+ tristate "XCeive xc2028/xc3028 tuners"
+ depends on I2C
+ default m if VIDEO_TUNER_CUSTOMIZE
+ help
+ Say Y here to include support for the xc2028/xc3028 tuners.
+
config TUNER_MT20XX
tristate "Microtune 2032 / 2050 tuners"
depends on I2C
@@ -97,8 +106,10 @@ config TUNER_MT20XX
Say Y here to include support for the MT2032 / MT2050 tuner.
config TUNER_TDA8290
- tristate "TDA 8290+8275(a) tuner combo"
+ tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
depends on I2C
+ select DVB_TDA827X
+ select DVB_TDA18271
default m if VIDEO_TUNER_CUSTOMIZE
help
Say Y here to include support for Philips TDA8290+8275(a) tuner.
@@ -120,10 +131,19 @@ config TUNER_TEA5767
config TUNER_SIMPLE
tristate "Simple tuner support"
depends on I2C
+ select TUNER_TDA9887
default m if VIDEO_TUNER_CUSTOMIZE
help
Say Y here to include support for various simple tuners.
+config TUNER_TDA9887
+ tristate "TDA 9885/6/7 analog IF demodulator"
+ depends on I2C
+ default m if VIDEO_TUNER_CUSTOMIZE
+ help
+ Say Y here to include support for Philips TDA9885/6/7
+ analog IF demodulator.
+
endif # VIDEO_TUNER_CUSTOMIZE
config VIDEOBUF_GEN
diff --git a/trunk/drivers/media/common/Kconfig b/trunk/drivers/media/common/Kconfig
index c5092ef1082f..06ca75911b7f 100644
--- a/trunk/drivers/media/common/Kconfig
+++ b/trunk/drivers/media/common/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_SAA7146
tristate
- depends on I2C
+ depends on I2C && PCI
config VIDEO_SAA7146_VV
tristate
diff --git a/trunk/drivers/media/common/ir-functions.c b/trunk/drivers/media/common/ir-functions.c
index e7c3ab951a44..bb2a027b9483 100644
--- a/trunk/drivers/media/common/ir-functions.c
+++ b/trunk/drivers/media/common/ir-functions.c
@@ -258,7 +258,7 @@ int ir_decode_biphase(u32 *samples, int count, int low, int high)
* saa7134 */
/* decode raw bit pattern to RC5 code */
-u32 ir_rc5_decode(unsigned int code)
+static u32 ir_rc5_decode(unsigned int code)
{
unsigned int org_code = code;
unsigned int pair;
@@ -371,7 +371,6 @@ EXPORT_SYMBOL_GPL(ir_dump_samples);
EXPORT_SYMBOL_GPL(ir_decode_biphase);
EXPORT_SYMBOL_GPL(ir_decode_pulsedistance);
-EXPORT_SYMBOL_GPL(ir_rc5_decode);
EXPORT_SYMBOL_GPL(ir_rc5_timer_end);
EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup);
diff --git a/trunk/drivers/media/common/ir-keymaps.c b/trunk/drivers/media/common/ir-keymaps.c
index 185e8a860c1a..a4a937c90534 100644
--- a/trunk/drivers/media/common/ir-keymaps.c
+++ b/trunk/drivers/media/common/ir-keymaps.c
@@ -1331,7 +1331,12 @@ IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = {
[ 0x35 ] = KEY_FASTFORWARD,
[ 0x36 ] = KEY_TV,
[ 0x37 ] = KEY_RADIO, /* FM */
- [ 0x38 ] = KEY_DVD
+ [ 0x38 ] = KEY_DVD,
+
+ [ 0x3e ] = KEY_F21, /* MCE +VOL, on Y04G0033 */
+ [ 0x3a ] = KEY_F22, /* MCE -VOL, on Y04G0033 */
+ [ 0x3b ] = KEY_F23, /* MCE +CH, on Y04G0033 */
+ [ 0x3f ] = KEY_F24 /* MCE -CH, on Y04G0033 */
};
EXPORT_SYMBOL_GPL(ir_codes_winfast);
@@ -1843,3 +1848,142 @@ IR_KEYTAB_TYPE ir_codes_fusionhdtv_mce[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_fusionhdtv_mce);
+
+/* Pinnacle PCTV HD 800i mini remote */
+IR_KEYTAB_TYPE ir_codes_pinnacle_pctv_hd[IR_KEYTAB_SIZE] = {
+
+ [0x0f] = KEY_1,
+ [0x15] = KEY_2,
+ [0x10] = KEY_3,
+ [0x18] = KEY_4,
+ [0x1b] = KEY_5,
+ [0x1e] = KEY_6,
+ [0x11] = KEY_7,
+ [0x21] = KEY_8,
+ [0x12] = KEY_9,
+ [0x27] = KEY_0,
+
+ [0x24] = KEY_ZOOM,
+ [0x2a] = KEY_SUBTITLE,
+
+ [0x00] = KEY_MUTE,
+ [0x01] = KEY_ENTER, /* Pinnacle Logo */
+ [0x39] = KEY_POWER,
+
+ [0x03] = KEY_VOLUMEUP,
+ [0x09] = KEY_VOLUMEDOWN,
+ [0x06] = KEY_CHANNELUP,
+ [0x0c] = KEY_CHANNELDOWN,
+
+ [0x2d] = KEY_REWIND,
+ [0x30] = KEY_PLAYPAUSE,
+ [0x33] = KEY_FASTFORWARD,
+ [0x3c] = KEY_STOP,
+ [0x36] = KEY_RECORD,
+ [0x3f] = KEY_EPG, /* Labeled "?" */
+};
+EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd);
+
+/*
+ * Igor Kuznetsov
+ * Andrey J. Melnikov
+ *
+ * Keytable is used by BeholdTV 60x series, M6 series at
+ * least, and probably other cards too.
+ * The "ascii-art picture" below (in comments, first row
+ * is the keycode in hex, and subsequent row(s) shows
+ * the button labels (several variants when appropriate)
+ * helps to descide which keycodes to assign to the buttons.
+ */
+IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = {
+
+ /* 0x1c 0x12 *
+ * TV/FM POWER *
+ * */
+ [ 0x1c ] = KEY_TUNER, /*XXX KEY_TV KEY_RADIO */
+ [ 0x12 ] = KEY_POWER,
+
+ /* 0x01 0x02 0x03 *
+ * 1 2 3 *
+ * *
+ * 0x04 0x05 0x06 *
+ * 4 5 6 *
+ * *
+ * 0x07 0x08 0x09 *
+ * 7 8 9 *
+ * */
+ [ 0x01 ] = KEY_1,
+ [ 0x02 ] = KEY_2,
+ [ 0x03 ] = KEY_3,
+ [ 0x04 ] = KEY_4,
+ [ 0x05 ] = KEY_5,
+ [ 0x06 ] = KEY_6,
+ [ 0x07 ] = KEY_7,
+ [ 0x08 ] = KEY_8,
+ [ 0x09 ] = KEY_9,
+
+ /* 0x0a 0x00 0x17 *
+ * RECALL 0 MODE *
+ * */
+ [ 0x0a ] = KEY_AGAIN,
+ [ 0x00 ] = KEY_0,
+ [ 0x17 ] = KEY_MODE,
+
+ /* 0x14 0x10 *
+ * ASPECT FULLSCREEN *
+ * */
+ [ 0x14 ] = KEY_SCREEN,
+ [ 0x10 ] = KEY_ZOOM,
+
+ /* 0x0b *
+ * Up *
+ * *
+ * 0x18 0x16 0x0c *
+ * Left Ok Right *
+ * *
+ * 0x015 *
+ * Down *
+ * */
+ [ 0x0b ] = KEY_CHANNELUP, /*XXX KEY_UP */
+ [ 0x18 ] = KEY_VOLUMEDOWN, /*XXX KEY_LEFT */
+ [ 0x16 ] = KEY_OK, /*XXX KEY_ENTER */
+ [ 0x0c ] = KEY_VOLUMEUP, /*XXX KEY_RIGHT */
+ [ 0x15 ] = KEY_CHANNELDOWN, /*XXX KEY_DOWN */
+
+ /* 0x11 0x0d *
+ * MUTE INFO *
+ * */
+ [ 0x11 ] = KEY_MUTE,
+ [ 0x0d ] = KEY_INFO,
+
+ /* 0x0f 0x1b 0x1a *
+ * RECORD PLAY/PAUSE STOP *
+ * *
+ * 0x0e 0x1f 0x1e *
+ *TELETEXT AUDIO SOURCE *
+ * RED YELLOW *
+ * */
+ [ 0x0f ] = KEY_RECORD,
+ [ 0x1b ] = KEY_PLAYPAUSE,
+ [ 0x1a ] = KEY_STOP,
+ [ 0x0e ] = KEY_TEXT,
+ [ 0x1f ] = KEY_RED, /*XXX KEY_AUDIO */
+ [ 0x1e ] = KEY_YELLOW, /*XXX KEY_SOURCE */
+
+ /* 0x1d 0x13 0x19 *
+ * SLEEP PREVIEW DVB *
+ * GREEN BLUE *
+ * */
+ [ 0x1d ] = KEY_SLEEP,
+ [ 0x13 ] = KEY_GREEN,
+ [ 0x19 ] = KEY_BLUE, /*XXX KEY_SAT */
+
+ /* 0x58 0x5c *
+ * FREEZE SNAPSHOT *
+ * */
+ [ 0x58 ] = KEY_SLOW,
+ [ 0x5c ] = KEY_SAVE,
+
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_behold);
diff --git a/trunk/drivers/media/common/saa7146_fops.c b/trunk/drivers/media/common/saa7146_fops.c
index 67d1b1b1b254..f0703d8bc3e8 100644
--- a/trunk/drivers/media/common/saa7146_fops.c
+++ b/trunk/drivers/media/common/saa7146_fops.c
@@ -61,7 +61,7 @@ void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
videobuf_waiton(&buf->vb,0,0);
videobuf_dma_unmap(q, dma);
videobuf_dma_free(dma);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
@@ -83,7 +83,7 @@ int saa7146_buffer_queue(struct saa7146_dev *dev,
buf->activate(dev,buf,NULL);
} else {
list_add_tail(&buf->vb.queue,&q->queue);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
}
return 0;
@@ -174,7 +174,7 @@ void saa7146_buffer_timeout(unsigned long data)
spin_lock_irqsave(&dev->slock,flags);
if (q->curr) {
DEB_D(("timeout on %p\n", q->curr));
- saa7146_buffer_finish(dev,q,STATE_ERROR);
+ saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
}
/* we don't restart the transfer here like other drivers do. when
@@ -366,7 +366,7 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
}
poll_wait(file, &buf->done, wait);
- if (buf->state == STATE_DONE || buf->state == STATE_ERROR) {
+ if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
DEB_D(("poll succeeded!\n"));
return POLLIN|POLLRDNORM;
}
@@ -538,6 +538,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
// fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
if (video_register_device(vfd, type, -1) < 0) {
ERR(("cannot register v4l2 device. skipping.\n"));
+ video_device_release(vfd);
return -1;
}
diff --git a/trunk/drivers/media/common/saa7146_vbi.c b/trunk/drivers/media/common/saa7146_vbi.c
index 6103484e4442..c32dda973e92 100644
--- a/trunk/drivers/media/common/saa7146_vbi.c
+++ b/trunk/drivers/media/common/saa7146_vbi.c
@@ -205,7 +205,7 @@ static int buffer_activate(struct saa7146_dev *dev,
struct saa7146_buf *next)
{
struct saa7146_vv *vv = dev->vv_data;
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next));
saa7146_set_vbi_capture(dev,buf,next);
@@ -238,7 +238,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e
if (buf->vb.size != size)
saa7146_dma_free(dev,q,buf);
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
buf->vb.width = llength;
@@ -257,7 +257,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e
if (0 != err)
return err;
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
return 0;
@@ -335,7 +335,7 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file)
saa7146_write(dev, MC1, MASK_20);
if (vv->vbi_q.curr) {
- saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
+ saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE);
}
videobuf_queue_cancel(&fh->vbi_q);
@@ -458,7 +458,7 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
/* this must be += 2, one count for each field */
vv->vbi_fieldcount+=2;
vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount;
- saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
+ saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE);
} else {
DEB_VBI(("dev:%p\n",dev));
}
diff --git a/trunk/drivers/media/common/saa7146_video.c b/trunk/drivers/media/common/saa7146_video.c
index ae36d101006b..c31ab480d8e1 100644
--- a/trunk/drivers/media/common/saa7146_video.c
+++ b/trunk/drivers/media/common/saa7146_video.c
@@ -1235,7 +1235,7 @@ static int buffer_activate (struct saa7146_dev *dev,
{
struct saa7146_vv *vv = dev->vv_data;
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
saa7146_set_capture(dev,buf,next);
mod_timer(&vv->video_q.timeout, jiffies+BUFFER_TIMEOUT);
@@ -1281,7 +1281,7 @@ static int buffer_prepare(struct videobuf_queue *q,
saa7146_dma_free(dev,q,buf);
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct saa7146_format *sfmt;
buf->vb.bytesperline = fh->video_fmt.bytesperline;
@@ -1314,7 +1314,7 @@ static int buffer_prepare(struct videobuf_queue *q,
if (err)
goto oops;
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
return 0;
@@ -1453,7 +1453,7 @@ static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
/* only finish the buffer if we have one... */
if( NULL != q->curr ) {
- saa7146_buffer_finish(dev,q,STATE_DONE);
+ saa7146_buffer_finish(dev,q,VIDEOBUF_DONE);
}
saa7146_buffer_next(dev,q,0);
diff --git a/trunk/drivers/media/dvb/b2c2/flexcop.c b/trunk/drivers/media/dvb/b2c2/flexcop.c
index 29ec4183118e..2ddafd071c97 100644
--- a/trunk/drivers/media/dvb/b2c2/flexcop.c
+++ b/trunk/drivers/media/dvb/b2c2/flexcop.c
@@ -212,7 +212,6 @@ void flexcop_reset_block_300(struct flexcop_device *fc)
fc->write_ibi_reg(fc,ctrl_208,v208_save);
}
-EXPORT_SYMBOL(flexcop_reset_block_300);
struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
{
diff --git a/trunk/drivers/media/dvb/bt8xx/bt878.c b/trunk/drivers/media/dvb/bt8xx/bt878.c
index 85e36a1d6d78..c7bbb40223f5 100644
--- a/trunk/drivers/media/dvb/bt8xx/bt878.c
+++ b/trunk/drivers/media/dvb/bt8xx/bt878.c
@@ -378,23 +378,37 @@ bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *
EXPORT_SYMBOL(bt878_device_control);
+#define BROOKTREE_878_DEVICE(vend, dev, name) \
+ { \
+ .vendor = PCI_VENDOR_ID_BROOKTREE, \
+ .device = PCI_DEVICE_ID_BROOKTREE_878, \
+ .subvendor = (vend), .subdevice = (dev), \
+ .driver_data = (unsigned long) name \
+ }
-static struct cards card_list[] __devinitdata = {
-
- { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
- { 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" },
- { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" },
- { 0x002611bd, BTTV_BOARD_TWINHAN_DST, "Pinnacle PCTV SAT CI" },
- { 0x00011822, BTTV_BOARD_TWINHAN_DST, "Twinhan VisionPlus DVB" },
- { 0xfc00270f, BTTV_BOARD_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" },
- { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" },
- { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" },
- { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" },
- { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" },
- { 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV" },
- { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" }
+static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
+ BROOKTREE_878_DEVICE(0x0071, 0x0101, "Nebula Electronics DigiTV"),
+ BROOKTREE_878_DEVICE(0x1461, 0x0761, "AverMedia AverTV DVB-T 761"),
+ BROOKTREE_878_DEVICE(0x11bd, 0x001c, "Pinnacle PCTV Sat"),
+ BROOKTREE_878_DEVICE(0x11bd, 0x0026, "Pinnacle PCTV SAT CI"),
+ BROOKTREE_878_DEVICE(0x1822, 0x0001, "Twinhan VisionPlus DVB"),
+ BROOKTREE_878_DEVICE(0x270f, 0xfc00,
+ "ChainTech digitop DST-1000 DVB-S"),
+ BROOKTREE_878_DEVICE(0x1461, 0x0771, "AVermedia AverTV DVB-T 771"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xdb10, "DViCO FusionHDTV DVB-T Lite"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xdb11, "Ultraview DVB-T Lite"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xd500, "DViCO FusionHDTV 5 Lite"),
+ BROOKTREE_878_DEVICE(0x7063, 0x2000, "pcHDTV HD-2000 TV"),
+ BROOKTREE_878_DEVICE(0x1822, 0x0026, "DNTV Live! Mini"),
+ { }
};
+MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
+
+static const char * __devinit card_name(const struct pci_device_id *id)
+{
+ return id->driver_data ? (const char *)id->driver_data : "Unknown";
+}
/***********************/
/* PCI device handling */
@@ -403,15 +417,13 @@ static struct cards card_list[] __devinitdata = {
static int __devinit bt878_probe(struct pci_dev *dev,
const struct pci_device_id *pci_id)
{
- int result = 0, has_dvb = 0, i;
+ int result = 0;
unsigned char lat;
struct bt878 *bt;
#if defined(__powerpc__)
unsigned int cmd;
#endif
unsigned int cardid;
- unsigned short id;
- struct cards *dvb_cards;
printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n",
bt878_num);
@@ -423,25 +435,11 @@ static int __devinit bt878_probe(struct pci_dev *dev,
if (pci_enable_device(dev))
return -EIO;
- pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &id);
- cardid = id << 16;
- pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &id);
- cardid |= id;
-
- for (i = 0, dvb_cards = card_list; i < ARRAY_SIZE(card_list); i++, dvb_cards++) {
- if (cardid == dvb_cards->pci_id) {
- printk("%s: card id=[0x%x],[ %s ] has DVB functions.\n",
- __func__, cardid, dvb_cards->name);
- has_dvb = 1;
- }
- }
+ cardid = dev->subsystem_device << 16;
+ cardid |= dev->subsystem_vendor;
- if (!has_dvb) {
- printk("%s: card id=[0x%x], Unknown card.\nExiting..\n", __func__, cardid);
- result = -EINVAL;
-
- goto fail0;
- }
+ printk(KERN_INFO "%s: card id=[0x%x],[ %s ] has DVB functions.\n",
+ __func__, cardid, card_name(pci_id));
bt = &bt878[bt878_num];
bt->dev = dev;
@@ -572,14 +570,6 @@ static void __devexit bt878_remove(struct pci_dev *pci_dev)
return;
}
-static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
- {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BROOKTREE_878,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0,}
-};
-
-MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
-
static struct pci_driver bt878_pci_driver = {
.name = "bt878",
.id_table = bt878_pci_tbl,
diff --git a/trunk/drivers/media/dvb/bt8xx/bt878.h b/trunk/drivers/media/dvb/bt8xx/bt878.h
index d593bc145628..375fd2892a11 100644
--- a/trunk/drivers/media/dvb/bt8xx/bt878.h
+++ b/trunk/drivers/media/dvb/bt8xx/bt878.h
@@ -101,12 +101,6 @@
#define BTTV_BOARD_DVICO_DVBT_LITE 0x80
#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87
-struct cards {
- __u32 pci_id;
- __u16 card_id;
- char *name;
-};
-
extern int bt878_num;
struct bt878 {
diff --git a/trunk/drivers/media/dvb/bt8xx/dst.c b/trunk/drivers/media/dvb/bt8xx/dst.c
index b7a17e69ca4d..307ff35bdf13 100644
--- a/trunk/drivers/media/dvb/bt8xx/dst.c
+++ b/trunk/drivers/media/dvb/bt8xx/dst.c
@@ -71,6 +71,7 @@ MODULE_PARM_DESC(dst_algo, "tuning algo: default is 0=(SW), 1=(HW)");
} \
} while(0)
+static int dst_command(struct dst_state *state, u8 *data, u8 len);
static void dst_packsize(struct dst_state *state, int psize)
{
@@ -80,7 +81,8 @@ static void dst_packsize(struct dst_state *state, int psize)
bt878_device_control(state->bt, DST_IG_TS, &bits);
}
-int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb, u32 outhigh, int delay)
+static int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb,
+ u32 outhigh, int delay)
{
union dst_gpio_packet enb;
union dst_gpio_packet bits;
@@ -109,9 +111,8 @@ int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb, u32 outhigh, int
return 0;
}
-EXPORT_SYMBOL(dst_gpio_outb);
-int dst_gpio_inb(struct dst_state *state, u8 *result)
+static int dst_gpio_inb(struct dst_state *state, u8 *result)
{
union dst_gpio_packet rd_packet;
int err;
@@ -125,7 +126,6 @@ int dst_gpio_inb(struct dst_state *state, u8 *result)
return 0;
}
-EXPORT_SYMBOL(dst_gpio_inb);
int rdc_reset_state(struct dst_state *state)
{
@@ -145,7 +145,7 @@ int rdc_reset_state(struct dst_state *state)
}
EXPORT_SYMBOL(rdc_reset_state);
-int rdc_8820_reset(struct dst_state *state)
+static int rdc_8820_reset(struct dst_state *state)
{
dprintk(verbose, DST_DEBUG, 1, "Resetting DST");
if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, 0, NO_DELAY) < 0) {
@@ -160,9 +160,8 @@ int rdc_8820_reset(struct dst_state *state)
return 0;
}
-EXPORT_SYMBOL(rdc_8820_reset);
-int dst_pio_enable(struct dst_state *state)
+static int dst_pio_enable(struct dst_state *state)
{
if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_ENABLE, 0, NO_DELAY) < 0) {
dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
@@ -172,7 +171,6 @@ int dst_pio_enable(struct dst_state *state)
return 0;
}
-EXPORT_SYMBOL(dst_pio_enable);
int dst_pio_disable(struct dst_state *state)
{
@@ -611,7 +609,7 @@ static int dst_type_print(struct dst_state *state, u8 type)
return 0;
}
-struct tuner_types tuner_list[] = {
+static struct tuner_types tuner_list[] = {
{
.tuner_type = TUNER_TYPE_L64724,
.tuner_name = "L 64724",
@@ -1224,7 +1222,7 @@ static int dst_probe(struct dst_state *state)
return 0;
}
-int dst_command(struct dst_state *state, u8 *data, u8 len)
+static int dst_command(struct dst_state *state, u8 *data, u8 len)
{
u8 reply;
@@ -1287,7 +1285,6 @@ int dst_command(struct dst_state *state, u8 *data, u8 len)
return -EIO;
}
-EXPORT_SYMBOL(dst_command);
static int dst_get_signal(struct dst_state *state)
{
diff --git a/trunk/drivers/media/dvb/bt8xx/dst_common.h b/trunk/drivers/media/dvb/bt8xx/dst_common.h
index 87623d203a89..d88cf2add82b 100644
--- a/trunk/drivers/media/dvb/bt8xx/dst_common.h
+++ b/trunk/drivers/media/dvb/bt8xx/dst_common.h
@@ -165,10 +165,8 @@ struct dst_config
};
int rdc_reset_state(struct dst_state *state);
-int rdc_8820_reset(struct dst_state *state);
int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode);
-int dst_pio_enable(struct dst_state *state);
int dst_pio_disable(struct dst_state *state);
int dst_error_recovery(struct dst_state* state);
int dst_error_bailout(struct dst_state *state);
@@ -179,9 +177,6 @@ int read_dst(struct dst_state *state, u8 * ret, u8 len);
u8 dst_check_sum(u8 * buf, u32 len);
struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter);
struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
-int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh, int delay);
-
-int dst_command(struct dst_state* state, u8 * data, u8 len);
#endif // DST_COMMON_H
diff --git a/trunk/drivers/media/dvb/dvb-core/dvb_frontend.c b/trunk/drivers/media/dvb/dvb-core/dvb_frontend.c
index 445f02665577..925cfa6221ad 100644
--- a/trunk/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/trunk/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -1202,6 +1202,10 @@ void dvb_frontend_detach(struct dvb_frontend* fe)
fe->ops.tuner_ops.release(fe);
symbol_put_addr(fe->ops.tuner_ops.release);
}
+ if (fe->ops.analog_ops.release) {
+ fe->ops.analog_ops.release(fe);
+ symbol_put_addr(fe->ops.analog_ops.release);
+ }
ptr = (void*)fe->ops.release;
if (ptr) {
fe->ops.release(fe);
@@ -1215,6 +1219,8 @@ void dvb_frontend_detach(struct dvb_frontend* fe)
fe->ops.release_sec(fe);
if (fe->ops.tuner_ops.release)
fe->ops.tuner_ops.release(fe);
+ if (fe->ops.analog_ops.release)
+ fe->ops.analog_ops.release(fe);
if (fe->ops.release)
fe->ops.release(fe);
}
diff --git a/trunk/drivers/media/dvb/dvb-core/dvb_frontend.h b/trunk/drivers/media/dvb/dvb-core/dvb_frontend.h
index a5262e852c82..aa4133f0bd19 100644
--- a/trunk/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/trunk/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -84,6 +84,9 @@ struct dvb_tuner_ops {
/** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */
int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len);
+ /** This is to allow setting tuner-specific configs */
+ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
+
int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
@@ -98,6 +101,28 @@ struct dvb_tuner_ops {
int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
};
+struct analog_demod_info {
+ char *name;
+};
+
+struct analog_demod_ops {
+
+ struct analog_demod_info info;
+
+ void (*set_params)(struct dvb_frontend *fe,
+ struct analog_parameters *params);
+ int (*has_signal)(struct dvb_frontend *fe);
+ int (*is_stereo)(struct dvb_frontend *fe);
+ int (*get_afc)(struct dvb_frontend *fe);
+ void (*tuner_status)(struct dvb_frontend *fe);
+ void (*standby)(struct dvb_frontend *fe);
+ void (*release)(struct dvb_frontend *fe);
+ int (*i2c_gate_ctrl)(struct dvb_frontend *fe, int enable);
+
+ /** This is to allow setting tuner-specific configuration */
+ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
+};
+
struct dvb_frontend_ops {
struct dvb_frontend_info info;
@@ -143,6 +168,7 @@ struct dvb_frontend_ops {
int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
struct dvb_tuner_ops tuner_ops;
+ struct analog_demod_ops analog_ops;
};
#define MAX_EVENT 8
@@ -159,18 +185,19 @@ struct dvb_fe_events {
struct dvb_frontend {
struct dvb_frontend_ops ops;
struct dvb_adapter *dvb;
- void* demodulator_priv;
- void* tuner_priv;
- void* frontend_priv;
- void* sec_priv;
+ void *demodulator_priv;
+ void *tuner_priv;
+ void *frontend_priv;
+ void *sec_priv;
+ void *analog_demod_priv;
};
-extern int dvb_register_frontend(struct dvb_adapter* dvb,
- struct dvb_frontend* fe);
+extern int dvb_register_frontend(struct dvb_adapter *dvb,
+ struct dvb_frontend *fe);
-extern int dvb_unregister_frontend(struct dvb_frontend* fe);
+extern int dvb_unregister_frontend(struct dvb_frontend *fe);
-extern void dvb_frontend_detach(struct dvb_frontend* fe);
+extern void dvb_frontend_detach(struct dvb_frontend *fe);
extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
diff --git a/trunk/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/trunk/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index 9878183ba3f0..ac9d93cf83c6 100644
--- a/trunk/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/trunk/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -261,11 +261,6 @@ EXPORT_SYMBOL(dvb_ringbuffer_init);
EXPORT_SYMBOL(dvb_ringbuffer_empty);
EXPORT_SYMBOL(dvb_ringbuffer_free);
EXPORT_SYMBOL(dvb_ringbuffer_avail);
-EXPORT_SYMBOL(dvb_ringbuffer_flush);
EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
EXPORT_SYMBOL(dvb_ringbuffer_read);
EXPORT_SYMBOL(dvb_ringbuffer_write);
-EXPORT_SYMBOL(dvb_ringbuffer_pkt_write);
-EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
-EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
-EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
diff --git a/trunk/drivers/media/dvb/dvb-usb/af9005.c b/trunk/drivers/media/dvb/dvb-usb/af9005.c
index 7db6eee50e39..e7f76f515b4f 100644
--- a/trunk/drivers/media/dvb/dvb-usb/af9005.c
+++ b/trunk/drivers/media/dvb/dvb-usb/af9005.c
@@ -1026,6 +1026,7 @@ static int af9005_usb_probe(struct usb_interface *intf,
static struct usb_device_id af9005_usb_table[] = {
{USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9005)},
{USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE)},
+ {USB_DEVICE(USB_VID_ANSONIC, USB_PID_ANSONIC_DVBT_USB)},
{0},
};
@@ -1075,7 +1076,7 @@ static struct dvb_usb_device_properties af9005_properties = {
.rc_key_map_size = 0,
.rc_query = af9005_rc_query,
- .num_device_descs = 2,
+ .num_device_descs = 3,
.devices = {
{.name = "Afatech DVB-T USB1.1 stick",
.cold_ids = {&af9005_usb_table[0], NULL},
@@ -1085,6 +1086,10 @@ static struct dvb_usb_device_properties af9005_properties = {
.cold_ids = {&af9005_usb_table[1], NULL},
.warm_ids = {NULL},
},
+ {.name = "Ansonic DVB-T USB1.1 stick",
+ .cold_ids = {&af9005_usb_table[2], NULL},
+ .warm_ids = {NULL},
+ },
{NULL},
}
};
diff --git a/trunk/drivers/media/dvb/dvb-usb/au6610.c b/trunk/drivers/media/dvb/dvb-usb/au6610.c
index 18e0b16fb2a9..f3ff81314696 100644
--- a/trunk/drivers/media/dvb/dvb-usb/au6610.c
+++ b/trunk/drivers/media/dvb/dvb-usb/au6610.c
@@ -79,12 +79,12 @@ static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
if (num > 2)
return -EINVAL;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
diff --git a/trunk/drivers/media/dvb/dvb-usb/cxusb.c b/trunk/drivers/media/dvb/dvb-usb/cxusb.c
index 04e31cf7d530..c58365005ac1 100644
--- a/trunk/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/trunk/drivers/media/dvb/dvb-usb/cxusb.c
@@ -15,7 +15,7 @@
*
* Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
* Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org)
- * Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au)
+ * Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au)
*
* 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
@@ -30,11 +30,16 @@
#include "mt352.h"
#include "mt352_priv.h"
#include "zl10353.h"
+#include "tuner-xc2028.h"
+#include "tuner-xc2028-types.h"
/* debug */
-int dvb_usb_cxusb_debug;
+static int dvb_usb_cxusb_debug;
module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
+#define deb_info(args...) dprintk(dvb_usb_cxusb_debug,0x01,args)
+#define deb_i2c(args...) if (d->udev->descriptor.idVendor == USB_VID_MEDION) \
+ dprintk(dvb_usb_cxusb_debug,0x01,args)
static int cxusb_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
@@ -46,11 +51,9 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d,
sndbuf[0] = cmd;
memcpy(&sndbuf[1], wbuf, wlen);
if (wo)
- dvb_usb_generic_write(d, sndbuf, 1+wlen);
+ return dvb_usb_generic_write(d, sndbuf, 1+wlen);
else
- dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
-
- return 0;
+ return dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
}
/* GPIO */
@@ -72,6 +75,34 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
st->gpio_write_state[GPIO_TUNER] = onoff;
}
+static int cxusb_bluebird_gpio_rw(struct dvb_usb_device *d, u8 changemask,
+ u8 newval)
+{
+ u8 o[2], gpio_state;
+ int rc;
+
+ o[0] = 0xff & ~changemask; /* mask of bits to keep */
+ o[1] = newval & changemask; /* new values for bits */
+
+ rc = cxusb_ctrl_msg(d, CMD_BLUEBIRD_GPIO_RW, o, 2, &gpio_state, 1);
+ if (rc < 0 || (gpio_state & changemask) != (newval & changemask))
+ deb_info("bluebird_gpio_write failed.\n");
+
+ return rc < 0 ? rc : gpio_state;
+}
+
+static void cxusb_bluebird_gpio_pulse(struct dvb_usb_device *d, u8 pin, int low)
+{
+ cxusb_bluebird_gpio_rw(d, pin, low ? 0 : pin);
+ msleep(5);
+ cxusb_bluebird_gpio_rw(d, pin, low ? pin : 0);
+}
+
+static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff)
+{
+ cxusb_bluebird_gpio_rw(d, 0x40, onoff ? 0 : 0x40);
+}
+
/* I2C */
static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
@@ -82,9 +113,6 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
- if (num > 2)
- warn("more than two i2c messages at a time is not handled yet. TODO.");
-
for (i = 0; i < num; i++) {
if (d->udev->descriptor.idVendor == USB_VID_MEDION)
@@ -97,8 +125,22 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
break;
}
- /* read request */
- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].flags & I2C_M_RD) {
+ /* read only */
+ u8 obuf[3], ibuf[1+msg[i].len];
+ obuf[0] = 0;
+ obuf[1] = msg[i].len;
+ obuf[2] = msg[i].addr;
+ if (cxusb_ctrl_msg(d, CMD_I2C_READ,
+ obuf, 3,
+ ibuf, 1+msg[i].len) < 0) {
+ warn("i2c read failed");
+ break;
+ }
+ memcpy(msg[i].buf, &ibuf[1], msg[i].len);
+ } else if (i+1 < num && (msg[i+1].flags & I2C_M_RD) &&
+ msg[i].addr == msg[i+1].addr) {
+ /* write to then read from same address */
u8 obuf[3+msg[i].len], ibuf[1+msg[i+1].len];
obuf[0] = msg[i].len;
obuf[1] = msg[i+1].len;
@@ -116,7 +158,8 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
i++;
- } else { /* write */
+ } else {
+ /* write only */
u8 obuf[2+msg[i].len], ibuf;
obuf[0] = msg[i].addr;
obuf[1] = msg[i].len;
@@ -131,7 +174,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
}
mutex_unlock(&d->i2c_mutex);
- return i;
+ return i == num ? num : -EREMOTEIO;
}
static u32 cxusb_i2c_func(struct i2c_adapter *adapter)
@@ -162,6 +205,17 @@ static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff)
return 0;
}
+static int cxusb_nano2_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int rc = 0;
+
+ rc = cxusb_power_ctrl(d, onoff);
+ if (!onoff)
+ cxusb_nano2_led(d, 0);
+
+ return rc;
+}
+
static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
u8 buf[2] = { 0x03, 0x00 };
@@ -197,6 +251,34 @@ static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
return 0;
}
+static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event,
+ int *state)
+{
+ struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+ u8 ircode[4];
+ int i;
+ struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD,
+ .buf = ircode, .len = 4 };
+
+ *event = 0;
+ *state = REMOTE_NO_KEY_PRESSED;
+
+ if (cxusb_i2c_xfer(&d->i2c_adap, &msg, 1) != 1)
+ return 0;
+
+ for (i = 0; i < d->props.rc_key_map_size; i++) {
+ if (keymap[i].custom == ircode[1] &&
+ keymap[i].data == ircode[2]) {
+ *event = keymap[i].event;
+ *state = REMOTE_KEY_PRESSED;
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
static struct dvb_usb_rc_key dvico_mce_rc_keys[] = {
{ 0xfe, 0x02, KEY_TV },
{ 0xfe, 0x0e, KEY_MP3 },
@@ -351,6 +433,20 @@ static struct mt352_config cxusb_mt352_config = {
.demod_init = cxusb_mt352_demod_init,
};
+static struct zl10353_config cxusb_zl10353_xc3028_config = {
+ .demod_address = 0x0f,
+ .if2 = 45600,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+};
+
+static struct mt352_config cxusb_mt352_xc3028_config = {
+ .demod_address = 0x0f,
+ .if2 = 4560,
+ .no_tuner = 1,
+ .demod_init = cxusb_mt352_demod_init,
+};
+
/* Callbacks for DVB USB */
static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
@@ -386,6 +482,51 @@ static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static int dvico_bluebird_xc2028_callback(void *ptr, int command, int arg)
+{
+ struct dvb_usb_device *d = ptr;
+
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ deb_info("%s: XC2028_TUNER_RESET %d\n", __FUNCTION__, arg);
+ cxusb_bluebird_gpio_pulse(d, 0x01, 1);
+ break;
+ case XC2028_RESET_CLK:
+ deb_info("%s: XC2028_RESET_CLK %d\n", __FUNCTION__, arg);
+ break;
+ default:
+ deb_info("%s: unknown command %d, arg %d\n", __FUNCTION__,
+ command, arg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &adap->dev->i2c_adap,
+ .i2c_addr = 0x61,
+ .video_dev = adap->dev,
+ .callback = dvico_bluebird_xc2028_callback,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = "xc3028-dvico-au-01.fw",
+ .max_len = 64,
+ .scode_table = ZARLINK456,
+ };
+
+ fe = dvb_attach(xc2028_attach, adap->fe, &cfg);
+ if (fe == NULL || fe->ops.tuner_ops.set_config == NULL)
+ return -EIO;
+
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+
+ return 0;
+}
+
static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
{
u8 b;
@@ -447,27 +588,120 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
return -EIO;
}
+static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ u8 ircode[4];
+ int i;
+ struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD,
+ .buf = ircode, .len = 4 };
+
+ if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
+ err("set interface failed");
+
+ cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
+
+ /* reset the tuner and demodulator */
+ cxusb_bluebird_gpio_rw(adap->dev, 0x04, 0);
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1);
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
+
+ if ((adap->fe = dvb_attach(zl10353_attach,
+ &cxusb_zl10353_xc3028_config,
+ &adap->dev->i2c_adap)) == NULL)
+ return -EIO;
+
+ /* try to determine if there is no IR decoder on the I2C bus */
+ for (i = 0; adap->dev->props.rc_key_map != NULL && i < 5; i++) {
+ msleep(20);
+ if (cxusb_i2c_xfer(&adap->dev->i2c_adap, &msg, 1) != 1)
+ goto no_IR;
+ if (ircode[0] == 0 && ircode[1] == 0)
+ continue;
+ if (ircode[2] + ircode[3] != 0xff) {
+no_IR:
+ adap->dev->props.rc_key_map = NULL;
+ info("No IR receiver detected on this device.");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
+ err("set interface failed");
+
+ cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
+
+ /* reset the tuner and demodulator */
+ cxusb_bluebird_gpio_rw(adap->dev, 0x04, 0);
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1);
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
+
+ if ((adap->fe = dvb_attach(zl10353_attach,
+ &cxusb_zl10353_xc3028_config,
+ &adap->dev->i2c_adap)) != NULL)
+ return 0;
+
+ if ((adap->fe = dvb_attach(mt352_attach,
+ &cxusb_mt352_xc3028_config,
+ &adap->dev->i2c_adap)) != NULL)
+ return 0;
+
+ return -EIO;
+}
+
+/*
+ * DViCO has shipped two devices with the same USB ID, but only one of them
+ * needs a firmware download. Check the device class details to see if they
+ * have non-default values to decide whether the device is actually cold or
+ * not, and forget a match if it turns out we selected the wrong device.
+ */
+static int bluebird_fx2_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int wascold = *cold;
+
+ *cold = udev->descriptor.bDeviceClass == 0xff &&
+ udev->descriptor.bDeviceSubClass == 0xff &&
+ udev->descriptor.bDeviceProtocol == 0xff;
+
+ if (*cold && !wascold)
+ *desc = NULL;
+
+ return 0;
+}
+
/*
* DViCO bluebird firmware needs the "warm" product ID to be patched into the
* firmware file before download.
*/
-#define BLUEBIRD_01_ID_OFFSET 6638
+static const int dvico_firmware_id_offsets[] = { 6638, 3204 };
static int bluebird_patch_dvico_firmware_download(struct usb_device *udev,
const struct firmware *fw)
{
- if (fw->size < BLUEBIRD_01_ID_OFFSET + 4)
- return -EINVAL;
+ int pos;
+
+ for (pos = 0; pos < ARRAY_SIZE(dvico_firmware_id_offsets); pos++) {
+ int idoff = dvico_firmware_id_offsets[pos];
- if (fw->data[BLUEBIRD_01_ID_OFFSET] == (USB_VID_DVICO & 0xff) &&
- fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) {
+ if (fw->size < idoff + 4)
+ continue;
- fw->data[BLUEBIRD_01_ID_OFFSET + 2] =
- le16_to_cpu(udev->descriptor.idProduct) + 1;
- fw->data[BLUEBIRD_01_ID_OFFSET + 3] =
- le16_to_cpu(udev->descriptor.idProduct) >> 8;
+ if (fw->data[idoff] == (USB_VID_DVICO & 0xff) &&
+ fw->data[idoff + 1] == USB_VID_DVICO >> 8) {
+ fw->data[idoff + 2] =
+ le16_to_cpu(udev->descriptor.idProduct) + 1;
+ fw->data[idoff + 3] =
+ le16_to_cpu(udev->descriptor.idProduct) >> 8;
- return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
+ return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
+ }
}
return -EINVAL;
@@ -479,6 +713,9 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties;
static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
+static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties;
+static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties;
+static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties;
static int cxusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -487,7 +724,10 @@ static int cxusb_probe(struct usb_interface *intf,
dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_dee1601_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_lgz201_properties,THIS_MODULE,NULL) == 0 ||
- dvb_usb_device_init(intf,&cxusb_bluebird_dtt7579_properties,THIS_MODULE,NULL) == 0) {
+ dvb_usb_device_init(intf,&cxusb_bluebird_dtt7579_properties,THIS_MODULE,NULL) == 0 ||
+ dvb_usb_device_init(intf,&cxusb_bluebird_dualdig4_properties,THIS_MODULE,NULL) == 0 ||
+ dvb_usb_device_init(intf,&cxusb_bluebird_nano2_properties,THIS_MODULE,NULL) == 0 ||
+ dvb_usb_device_init(intf,&cxusb_bluebird_nano2_needsfirmware_properties,THIS_MODULE,NULL) == 0) {
return 0;
}
@@ -508,6 +748,9 @@ static struct usb_device_id cxusb_table [] = {
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -766,6 +1009,151 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
}
};
+static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_dualdig4_frontend_attach,
+ .tuner_attach = cxusb_dvico_xc3028_tuner_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = dvico_mce_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dvico_mce_rc_keys),
+ .rc_query = cxusb_bluebird2_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DViCO FusionHDTV DVB-T Dual Digital 4",
+ { NULL },
+ { &cxusb_table[13], NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+ .identify_state = bluebird_fx2_identify_state,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_nano2_frontend_attach,
+ .tuner_attach = cxusb_dvico_xc3028_tuner_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_nano2_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = dvico_portable_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys),
+ .rc_query = cxusb_bluebird2_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DViCO FusionHDTV DVB-T NANO2",
+ { NULL },
+ { &cxusb_table[14], NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .firmware = "dvb-usb-bluebird-02.fw",
+ .download_firmware = bluebird_patch_dvico_firmware_download,
+ .identify_state = bluebird_fx2_identify_state,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_nano2_frontend_attach,
+ .tuner_attach = cxusb_dvico_xc3028_tuner_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_nano2_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = dvico_portable_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys),
+ .rc_query = cxusb_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DViCO FusionHDTV DVB-T NANO2 w/o firmware",
+ { &cxusb_table[14], NULL },
+ { &cxusb_table[15], NULL },
+ },
+ }
+};
+
static struct usb_driver cxusb_driver = {
.name = "dvb_usb_cxusb",
.probe = cxusb_probe,
diff --git a/trunk/drivers/media/dvb/dvb-usb/cxusb.h b/trunk/drivers/media/dvb/dvb-usb/cxusb.h
index c8ef77554b00..4768a2c35517 100644
--- a/trunk/drivers/media/dvb/dvb-usb/cxusb.h
+++ b/trunk/drivers/media/dvb/dvb-usb/cxusb.h
@@ -4,12 +4,9 @@
#define DVB_USB_LOG_PREFIX "cxusb"
#include "dvb-usb.h"
-extern int dvb_usb_cxusb_debug;
-#define deb_info(args...) dprintk(dvb_usb_cxusb_debug,0x01,args)
-#define deb_i2c(args...) if (d->udev->descriptor.idVendor == USB_VID_MEDION) \
- dprintk(dvb_usb_cxusb_debug,0x01,args)
-
/* usb commands - some of it are guesses, don't have a reference yet */
+#define CMD_BLUEBIRD_GPIO_RW 0x05
+
#define CMD_I2C_WRITE 0x08
#define CMD_I2C_READ 0x09
diff --git a/trunk/drivers/media/dvb/dvb-usb/dib0700_core.c b/trunk/drivers/media/dvb/dvb-usb/dib0700_core.c
index 3ea294eb96bd..c9857d5c6982 100644
--- a/trunk/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/trunk/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -243,7 +243,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
u8 b[4];
b[0] = REQUEST_ENABLE_VIDEO;
- b[1] = 0x00;
+ b[1] = (onoff << 4) | 0x00; /* this bit gives a kind of command, rather than enabling something or not */
b[2] = (0x01 << 4); /* Master mode */
b[3] = 0x00;
@@ -256,9 +256,6 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
b[2] |= st->channel_state;
- if (st->channel_state) /* if at least one channel is active */
- b[1] = (0x01 << 4) | 0x00;
-
deb_info("data for streaming: %x %x\n",b[1],b[2]);
return dib0700_ctrl_wr(adap->dev, b, 4);
diff --git a/trunk/drivers/media/dvb/dvb-usb/dib0700_devices.c b/trunk/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 58452b52002c..e7093826e975 100644
--- a/trunk/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/trunk/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -94,12 +94,28 @@ static int bristol_frontend_attach(struct dvb_usb_adapter *adap)
(10 + adap->id) << 1, &bristol_dib3000mc_config[adap->id])) == NULL ? -ENODEV : 0;
}
+static int eeprom_read(struct i2c_adapter *adap,u8 adrs,u8 *pval)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = 0x50, .flags = 0, .buf = &adrs, .len = 1 },
+ { .addr = 0x50, .flags = I2C_M_RD, .buf = pval, .len = 1 },
+ };
+ if (i2c_transfer(adap, msg, 2) != 2) return -EREMOTEIO;
+ return 0;
+}
+
static int bristol_tuner_attach(struct dvb_usb_adapter *adap)
{
- struct dib0700_state *st = adap->dev->priv;
+ struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap;
struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1);
- return dvb_attach(mt2060_attach,adap->fe, tun_i2c, &bristol_mt2060_config[adap->id],
- st->mt2060_if1[adap->id]) == NULL ? -ENODEV : 0;
+ s8 a;
+ int if1=1220;
+ if (adap->dev->udev->descriptor.idVendor == USB_VID_HAUPPAUGE &&
+ adap->dev->udev->descriptor.idProduct == USB_PID_HAUPPAUGE_NOVA_T_500_2) {
+ if (!eeprom_read(prim_i2c,0x59 + adap->id,&a)) if1=1220+a;
+ }
+ return dvb_attach(mt2060_attach,adap->fe, tun_i2c,&bristol_mt2060_config[adap->id],
+ if1) == NULL ? -ENODEV : 0;
}
/* STK7700D: Pinnacle/Terratec/Hauppauge Dual DVB-T Diversity */
@@ -230,6 +246,27 @@ static struct mt2266_config stk7700d_mt2266_config[2] = {
}
};
+static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ if (adap->id == 0) {
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(10);
+ dib7000p_i2c_enumeration(&adap->dev->i2c_adap,1,18,stk7700d_dib7000p_mt2266_config);
+ }
+
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1),
+ &stk7700d_dib7000p_mt2266_config[adap->id]);
+
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap)
{
if (adap->id == 0) {
@@ -415,6 +452,35 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = {
{ 0x1e, 0x38, KEY_YELLOW },
{ 0x1e, 0x3b, KEY_GOTO },
{ 0x1e, 0x3d, KEY_POWER },
+
+ /* Key codes for the Leadtek Winfast DTV Dongle */
+ { 0x00, 0x42, KEY_POWER },
+ { 0x07, 0x7c, KEY_TUNER },
+ { 0x0f, 0x4e, KEY_PRINT }, /* PREVIEW */
+ { 0x08, 0x40, KEY_SCREEN }, /* full screen toggle*/
+ { 0x0f, 0x71, KEY_DOT }, /* frequency */
+ { 0x07, 0x43, KEY_0 },
+ { 0x0c, 0x41, KEY_1 },
+ { 0x04, 0x43, KEY_2 },
+ { 0x0b, 0x7f, KEY_3 },
+ { 0x0e, 0x41, KEY_4 },
+ { 0x06, 0x43, KEY_5 },
+ { 0x09, 0x7f, KEY_6 },
+ { 0x0d, 0x7e, KEY_7 },
+ { 0x05, 0x7c, KEY_8 },
+ { 0x0a, 0x40, KEY_9 },
+ { 0x0e, 0x4e, KEY_CLEAR },
+ { 0x04, 0x7c, KEY_CHANNEL }, /* show channel number */
+ { 0x0f, 0x41, KEY_LAST }, /* recall */
+ { 0x03, 0x42, KEY_MUTE },
+ { 0x06, 0x4c, KEY_RESERVED }, /* PIP button*/
+ { 0x01, 0x72, KEY_SHUFFLE }, /* SNAPSHOT */
+ { 0x0c, 0x4e, KEY_PLAYPAUSE }, /* TIMESHIFT */
+ { 0x0b, 0x70, KEY_RECORD },
+ { 0x03, 0x7d, KEY_VOLUMEUP },
+ { 0x01, 0x7d, KEY_VOLUMEDOWN },
+ { 0x02, 0x42, KEY_CHANNELUP },
+ { 0x00, 0x7d, KEY_CHANNELDOWN },
};
/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
@@ -578,16 +644,22 @@ static struct mt2060_config stk7700p_mt2060_config = {
static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap)
{
+ struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap;
struct dib0700_state *st = adap->dev->priv;
struct i2c_adapter *tun_i2c;
-
+ s8 a;
+ int if1=1220;
+ if (adap->dev->udev->descriptor.idVendor == USB_VID_HAUPPAUGE &&
+ adap->dev->udev->descriptor.idProduct == USB_PID_HAUPPAUGE_NOVA_T_STICK) {
+ if (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a;
+ }
if (st->is_dib7000pc)
tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
else
tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config,
- st->mt2060_if1[0]) == NULL ? -ENODEV : 0;
+ if1) == NULL ? -ENODEV : 0;
}
/* DIB7070 generic */
@@ -709,6 +781,8 @@ static struct dib7000p_config dib7070p_dib7000p_config = {
.agc_config_count = 1,
.agc = &dib7070_agc_config,
.bw = &dib7070_bw_config_12_mhz,
+ .tuner_is_baseband = 1,
+ .spur_protect = 1,
.gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
@@ -748,6 +822,8 @@ static struct dib7000p_config stk7070pd_dib7000p_config[2] = {
.agc_config_count = 1,
.agc = &dib7070_agc_config,
.bw = &dib7070_bw_config_12_mhz,
+ .tuner_is_baseband = 1,
+ .spur_protect = 1,
.gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
@@ -760,6 +836,8 @@ static struct dib7000p_config stk7070pd_dib7000p_config[2] = {
.agc_config_count = 1,
.agc = &dib7070_agc_config,
.bw = &dib7070_bw_config_12_mhz,
+ .tuner_is_baseband = 1,
+ .spur_protect = 1,
.gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
@@ -821,6 +899,12 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T) },
{ USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500_PC) },
/* 20 */{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_EXPRESS) },
+ { USB_DEVICE(USB_VID_GIGABYTE, USB_PID_GIGABYTE_U7000) },
+ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14BR) },
+ { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3000) },
+ { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3100) },
+/* 25 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_3) },
+ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_MYTV_T) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -862,7 +946,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 7,
+ .num_device_descs = 8,
.devices = {
{ "DiBcom STK7700P reference design",
{ &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] },
@@ -891,6 +975,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ "AVerMedia AVerTV DVB-T Express",
{ &dib0700_usb_id_table[20] },
{ NULL },
+ },
+ { "Gigabyte U7000",
+ { &dib0700_usb_id_table[21], NULL },
+ { NULL },
}
},
@@ -961,7 +1049,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ "DiBcom STK7700D reference design",
{ &dib0700_usb_id_table[14], NULL },
{ NULL },
- },
+ }
},
.rc_interval = DEFAULT_RC_INTERVAL,
@@ -969,6 +1057,25 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
.rc_query = dib0700_rc_query
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .frontend_attach = stk7700P2_frontend_attach,
+ .tuner_attach = stk7700d_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "ASUS My Cinema U3000 Mini DVBT Tuner",
+ { &dib0700_usb_id_table[23], NULL },
+ { NULL },
+ },
+ }
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
@@ -983,7 +1090,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 2,
+ .num_device_descs = 6,
.devices = {
{ "DiBcom STK7070P reference design",
{ &dib0700_usb_id_table[15], NULL },
@@ -993,7 +1100,29 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ &dib0700_usb_id_table[16], NULL },
{ NULL },
},
- }
+ { "Artec T14BR DVB-T",
+ { &dib0700_usb_id_table[22], NULL },
+ { NULL },
+ },
+ { "ASUS My Cinema U3100 Mini DVBT Tuner",
+ { &dib0700_usb_id_table[24], NULL },
+ { NULL },
+ },
+ { "Hauppauge Nova-T Stick",
+ { &dib0700_usb_id_table[25], NULL },
+ { NULL },
+ },
+ { "Hauppauge Nova-T MyTV.t",
+ { &dib0700_usb_id_table[26], NULL },
+ { NULL },
+ },
+ },
+
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_key_map = dib0700_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
+ .rc_query = dib0700_rc_query
+
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 2,
@@ -1024,7 +1153,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ "Pinnacle PCTV Dual DVB-T Diversity Stick",
{ &dib0700_usb_id_table[18], NULL },
{ NULL },
- },
+ }
}
},
};
diff --git a/trunk/drivers/media/dvb/dvb-usb/digitv.c b/trunk/drivers/media/dvb/dvb-usb/digitv.c
index bca1e0905739..3acbda4aa27e 100644
--- a/trunk/drivers/media/dvb/dvb-usb/digitv.c
+++ b/trunk/drivers/media/dvb/dvb-usb/digitv.c
@@ -17,9 +17,10 @@
#include "nxt6000.h"
/* debug */
-int dvb_usb_digitv_debug;
+static int dvb_usb_digitv_debug;
module_param_named(debug,dvb_usb_digitv_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
+#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args)
static int digitv_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 vv, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
diff --git a/trunk/drivers/media/dvb/dvb-usb/digitv.h b/trunk/drivers/media/dvb/dvb-usb/digitv.h
index 8b43e3db8691..908c09f4966b 100644
--- a/trunk/drivers/media/dvb/dvb-usb/digitv.h
+++ b/trunk/drivers/media/dvb/dvb-usb/digitv.h
@@ -8,9 +8,6 @@ struct digitv_state {
int is_nxt6000;
};
-extern int dvb_usb_digitv_debug;
-#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args)
-
/* protocol (from usblogging and the SDK:
*
* Always 7 bytes bulk message(s) for controlling
diff --git a/trunk/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/trunk/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 4fa3e895028a..aa4844ef875e 100644
--- a/trunk/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/trunk/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -15,7 +15,9 @@
#define USB_VID_ALCOR_MICRO 0x058f
#define USB_VID_ALINK 0x05e3
#define USB_VID_ANCHOR 0x0547
+#define USB_VID_ANSONIC 0x10b9
#define USB_VID_ANUBIS_ELECTRONIC 0x10fd
+#define USB_VID_ASUS 0x0b05
#define USB_VID_AVERMEDIA 0x07ca
#define USB_VID_COMPRO 0x185b
#define USB_VID_COMPRO_UNK 0x145f
@@ -44,12 +46,16 @@
#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
#define USB_VID_UNIWILL 0x1584
#define USB_VID_WIDEVIEW 0x14aa
+/* dom : pour gigabyte u7000 */
+#define USB_VID_GIGABYTE 0x1044
+
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
#define USB_PID_ADSTECH_USB2_WARM 0xa334
#define USB_PID_AFATECH_AF9005 0x9020
#define USB_VID_ALINK_DTU 0xf170
+#define USB_PID_ANSONIC_DVBT_USB 0x6000
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
@@ -69,6 +75,7 @@
#define USB_PID_DIBCOM_STK7700P 0x1e14
#define USB_PID_DIBCOM_STK7700P_PC 0x1e78
#define USB_PID_DIBCOM_STK7700D 0x1ef0
+#define USB_PID_DIBCOM_STK7700_U7000 0x7001
#define USB_PID_DIBCOM_STK7070P 0x1ebc
#define USB_PID_DIBCOM_STK7070PD 0x1ebe
#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
@@ -99,6 +106,7 @@
#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a
#define USB_PID_ARTEC_T14_COLD 0x810b
#define USB_PID_ARTEC_T14_WARM 0x810c
+#define USB_PID_ARTEC_T14BR 0x810f
#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
@@ -120,6 +128,8 @@
#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950
#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050
#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070
+#define USB_PID_HAUPPAUGE_MYTV_T 0x7080
#define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580
#define USB_PID_AVERMEDIA_EXPRESS 0xb568
#define USB_PID_AVERMEDIA_VOLAR 0xa807
@@ -143,6 +153,9 @@
#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59
+#define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78
+#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70
+#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71
#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55
#define USB_PID_MEDION_MD95700 0x0932
@@ -170,6 +183,9 @@
#define USB_PID_OPERA1_WARM 0x3829
#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514
#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513
-
+/* dom pour gigabyte u7000 */
+#define USB_PID_GIGABYTE_U7000 0x7001
+#define USB_PID_ASUS_U3000 0x171f
+#define USB_PID_ASUS_U3100 0x173f
#endif
diff --git a/trunk/drivers/media/dvb/dvb-usb/gl861.c b/trunk/drivers/media/dvb/dvb-usb/gl861.c
index f01d99c1c43c..6b99d9f4d5b3 100644
--- a/trunk/drivers/media/dvb/dvb-usb/gl861.c
+++ b/trunk/drivers/media/dvb/dvb-usb/gl861.c
@@ -56,12 +56,12 @@ static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
if (num > 2)
return -EINVAL;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
diff --git a/trunk/drivers/media/dvb/dvb-usb/gp8psk.c b/trunk/drivers/media/dvb/dvb-usb/gp8psk.c
index 92147ee3e14f..83e8535014c6 100644
--- a/trunk/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/trunk/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -171,22 +171,6 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff)
return 0;
}
-int gp8psk_bcm4500_reload(struct dvb_usb_device *d)
-{
- u8 buf;
- int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct);
- /* Turn off 8psk power */
- if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1))
- return -EINVAL;
- /* Turn On 8psk power */
- if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1))
- return -EINVAL;
- /* load BCM4500 firmware */
- if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
- if (gp8psk_load_bcm4500fw(d))
- return EINVAL;
- return 0;
-}
static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
diff --git a/trunk/drivers/media/dvb/dvb-usb/gp8psk.h b/trunk/drivers/media/dvb/dvb-usb/gp8psk.h
index e83a57506cfa..e5cd8149c23d 100644
--- a/trunk/drivers/media/dvb/dvb-usb/gp8psk.h
+++ b/trunk/drivers/media/dvb/dvb-usb/gp8psk.h
@@ -92,6 +92,5 @@ extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d);
extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen);
-extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d);
#endif
diff --git a/trunk/drivers/media/dvb/dvb-usb/opera1.c b/trunk/drivers/media/dvb/dvb-usb/opera1.c
index d7c04951ceab..21935bf7059e 100644
--- a/trunk/drivers/media/dvb/dvb-usb/opera1.c
+++ b/trunk/drivers/media/dvb/dvb-usb/opera1.c
@@ -10,7 +10,9 @@
* see Documentation/dvb/README.dvb-usb for more information
*/
-#include "opera1.h"
+#define DVB_USB_LOG_PREFIX "opera"
+
+#include "dvb-usb.h"
#include "stv0299.h"
#define OPERA_READ_MSG 0
@@ -38,7 +40,7 @@ struct opera_rc_keys {
u32 event;
};
-int dvb_usb_opera1_debug;
+static int dvb_usb_opera1_debug;
module_param_named(debug, dvb_usb_opera1_debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))."
diff --git a/trunk/drivers/media/dvb/dvb-usb/opera1.h b/trunk/drivers/media/dvb/dvb-usb/opera1.h
deleted file mode 100644
index 53174427902d..000000000000
--- a/trunk/drivers/media/dvb/dvb-usb/opera1.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _OPERA1_H_
-#define _OPERA1_H_
-
-#define DVB_USB_LOG_PREFIX "opera"
-#include "dvb-usb.h"
-
-extern int dvb_usb_opera1_debug;
-#define deb_xfer(args...) dprintk(dvb_usb_opera1_debug,0x02,args)
-#endif
diff --git a/trunk/drivers/media/dvb/dvb-usb/vp702x.c b/trunk/drivers/media/dvb/dvb-usb/vp702x.c
index 16533b31a82d..e553c139ac44 100644
--- a/trunk/drivers/media/dvb/dvb-usb/vp702x.c
+++ b/trunk/drivers/media/dvb/dvb-usb/vp702x.c
@@ -56,7 +56,7 @@ int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8
return ret;
}
-int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
+static int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
@@ -204,19 +204,6 @@ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
return 0;
}
-int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff)
-{
- struct vp702x_device_state *st = d->priv;
-
- if (st->power_state == 0 && onoff)
- vp702x_usb_out_op(d, SET_TUNER_POWER_REQ, 1, 7, NULL, 0);
- else if (st->power_state == 1 && onoff == 0)
- vp702x_usb_out_op(d, SET_TUNER_POWER_REQ, 0, 7, NULL, 0);
-
- st->power_state = onoff;
-
- return 0;
-}
static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
{
diff --git a/trunk/drivers/media/dvb/dvb-usb/vp702x.h b/trunk/drivers/media/dvb/dvb-usb/vp702x.h
index 25a9dee4c824..c2f97f96c21f 100644
--- a/trunk/drivers/media/dvb/dvb-usb/vp702x.h
+++ b/trunk/drivers/media/dvb/dvb-usb/vp702x.h
@@ -102,7 +102,5 @@ extern struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d);
extern int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec);
extern int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
-extern int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
-extern int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff);
#endif
diff --git a/trunk/drivers/media/dvb/dvb-usb/vp7045.c b/trunk/drivers/media/dvb/dvb-usb/vp7045.c
index 5bbd2d5192f0..c172babf59bb 100644
--- a/trunk/drivers/media/dvb/dvb-usb/vp7045.c
+++ b/trunk/drivers/media/dvb/dvb-usb/vp7045.c
@@ -15,9 +15,12 @@
#include "vp7045.h"
/* debug */
-int dvb_usb_vp7045_debug;
+static int dvb_usb_vp7045_debug;
module_param_named(debug,dvb_usb_vp7045_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
+#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args)
+#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args)
+#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args)
int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec)
{
diff --git a/trunk/drivers/media/dvb/dvb-usb/vp7045.h b/trunk/drivers/media/dvb/dvb-usb/vp7045.h
index 9ce21a20fa86..969688f85267 100644
--- a/trunk/drivers/media/dvb/dvb-usb/vp7045.h
+++ b/trunk/drivers/media/dvb/dvb-usb/vp7045.h
@@ -17,11 +17,6 @@
#define DVB_USB_LOG_PREFIX "vp7045"
#include "dvb-usb.h"
-extern int dvb_usb_vp7045_debug;
-#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args)
-#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args)
-#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args)
-
/* vp7045 commands */
/* Twinhan Vendor requests */
diff --git a/trunk/drivers/media/dvb/frontends/Kconfig b/trunk/drivers/media/dvb/frontends/Kconfig
index 59b9ed1f1aec..9ad86ce4a4e5 100644
--- a/trunk/drivers/media/dvb/frontends/Kconfig
+++ b/trunk/drivers/media/dvb/frontends/Kconfig
@@ -316,6 +316,13 @@ config DVB_TDA827X
help
A DVB-T silicon tuner module. Say Y when you want to support this tuner.
+config DVB_TDA18271
+ tristate "NXP TDA18271 silicon tuner"
+ depends on I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A silicon tuner module. Say Y when you want to support this tuner.
+
config DVB_TUNER_QT1010
tristate "Quantek QT1010 silicon tuner"
depends on DVB_CORE && I2C
@@ -353,6 +360,15 @@ config DVB_TUNER_DIB0070
This device is only used inside a SiP called togther with a
demodulator for now.
+config DVB_TUNER_XC5000
+ tristate "Xceive XC5000 silicon tuner"
+ depends on I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the silicon tuner XC5000 from Xceive.
+ This device is only used inside a SiP called togther with a
+ demodulator for now.
+
comment "Miscellaneous devices"
depends on DVB_CORE
diff --git a/trunk/drivers/media/dvb/frontends/Makefile b/trunk/drivers/media/dvb/frontends/Makefile
index 4b8ad1f132aa..16bd107ebd32 100644
--- a/trunk/drivers/media/dvb/frontends/Makefile
+++ b/trunk/drivers/media/dvb/frontends/Makefile
@@ -3,6 +3,9 @@
#
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/video/
+
+tda18271-objs := tda18271-tables.o tda18271-common.o tda18271-fe.o
obj-$(CONFIG_DVB_PLL) += dvb-pll.o
obj-$(CONFIG_DVB_STV0299) += stv0299.o
@@ -39,6 +42,7 @@ obj-$(CONFIG_DVB_ISL6421) += isl6421.o
obj-$(CONFIG_DVB_TDA10086) += tda10086.o
obj-$(CONFIG_DVB_TDA826X) += tda826x.o
obj-$(CONFIG_DVB_TDA827X) += tda827x.o
+obj-$(CONFIG_DVB_TDA18271) += tda18271.o
obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
obj-$(CONFIG_DVB_TUNER_MT2266) += mt2266.o
obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
@@ -46,3 +50,4 @@ obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
obj-$(CONFIG_DVB_TUA6100) += tua6100.o
obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o
obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
+obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
diff --git a/trunk/drivers/media/dvb/frontends/dib0070.c b/trunk/drivers/media/dvb/frontends/dib0070.c
index 481eaa684157..fe895bf7b18f 100644
--- a/trunk/drivers/media/dvb/frontends/dib0070.c
+++ b/trunk/drivers/media/dvb/frontends/dib0070.c
@@ -434,9 +434,14 @@ static u16 dib0070_p1f_defaults[] =
0,
};
-static void dib0070_wbd_calibration(struct dib0070_state *state)
+static void dib0070_wbd_calibration(struct dvb_frontend *fe)
{
u16 wbd_offs;
+ struct dib0070_state *state = fe->tuner_priv;
+
+ if (state->cfg->sleep)
+ state->cfg->sleep(fe, 0);
+
dib0070_write_reg(state, 0x0f, 0x6d81);
dib0070_write_reg(state, 0x20, 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
msleep(9);
@@ -444,6 +449,10 @@ static void dib0070_wbd_calibration(struct dib0070_state *state)
dib0070_write_reg(state, 0x20, 0);
state->wbd_ff_offset = ((wbd_offs * 8 * 18 / 33 + 1) / 2);
dprintk( "WBDStart = %d (Vargen) - FF = %hd", (u32) wbd_offs * 1800/1024, state->wbd_ff_offset);
+
+ if (state->cfg->sleep)
+ state->cfg->sleep(fe, 1);
+
}
u16 dib0070_wbd_offset(struct dvb_frontend *fe)
@@ -560,7 +569,7 @@ struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter
if (dib0070_reset(state) != 0)
goto free_mem;
- dib0070_wbd_calibration(state);
+ dib0070_wbd_calibration(fe);
printk(KERN_INFO "DiB0070: successfully identified\n");
memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));
diff --git a/trunk/drivers/media/dvb/frontends/dib3000mc.c b/trunk/drivers/media/dvb/frontends/dib3000mc.c
index edae0be063f5..fa851601e7d4 100644
--- a/trunk/drivers/media/dvb/frontends/dib3000mc.c
+++ b/trunk/drivers/media/dvb/frontends/dib3000mc.c
@@ -684,6 +684,9 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct dib3000mc_state *state = fe->demodulator_priv;
+ int ret;
+
+ dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
state->current_bandwidth = fep->u.ofdm.bandwidth;
dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));
@@ -700,7 +703,7 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe,
fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
fep->u.ofdm.constellation == QAM_AUTO ||
fep->u.ofdm.code_rate_HP == FEC_AUTO) {
- int i = 100, found;
+ int i = 1000, found;
dib3000mc_autosearch_start(fe, fep);
do {
@@ -715,10 +718,11 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe,
dib3000mc_get_frontend(fe, fep);
}
+ ret = dib3000mc_tune(fe, fep);
+
/* make this a config parameter */
dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
-
- return dib3000mc_tune(fe, fep);
+ return ret;
}
static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat)
diff --git a/trunk/drivers/media/dvb/frontends/dib7000m.c b/trunk/drivers/media/dvb/frontends/dib7000m.c
index fb18441a8c57..5f1375e30dfc 100644
--- a/trunk/drivers/media/dvb/frontends/dib7000m.c
+++ b/trunk/drivers/media/dvb/frontends/dib7000m.c
@@ -1171,7 +1171,9 @@ static int dib7000m_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct dib7000m_state *state = fe->demodulator_priv;
- int time;
+ int time, ret;
+
+ dib7000m_set_output_mode(state, OUTMODE_HIGH_Z);
state->current_bandwidth = fep->u.ofdm.bandwidth;
dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));
@@ -1206,10 +1208,11 @@ static int dib7000m_set_frontend(struct dvb_frontend* fe,
dib7000m_get_frontend(fe, fep);
}
+ ret = dib7000m_tune(fe, fep);
+
/* make this a config parameter */
dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO);
-
- return dib7000m_tune(fe, fep);
+ return ret;
}
static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat)
diff --git a/trunk/drivers/media/dvb/frontends/dib7000p.c b/trunk/drivers/media/dvb/frontends/dib7000p.c
index f45bcfc51cf8..47c23e29753e 100644
--- a/trunk/drivers/media/dvb/frontends/dib7000p.c
+++ b/trunk/drivers/media/dvb/frontends/dib7000p.c
@@ -35,8 +35,8 @@ struct dib7000p_state {
u16 wbd_ref;
- u8 current_band;
- fe_bandwidth_t current_bandwidth;
+ u8 current_band;
+ u32 current_bandwidth;
struct dibx000_agc_config *current_agc;
u32 timf;
@@ -1074,7 +1074,7 @@ static int dib7000p_get_frontend(struct dvb_frontend* fe,
fep->inversion = INVERSION_AUTO;
- fep->u.ofdm.bandwidth = state->current_bandwidth;
+ fep->u.ofdm.bandwidth = BANDWIDTH_TO_INDEX(state->current_bandwidth);
switch ((tps >> 8) & 0x3) {
case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
@@ -1128,12 +1128,11 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct dib7000p_state *state = fe->demodulator_priv;
- int time;
+ int time, ret;
- state->current_bandwidth = fep->u.ofdm.bandwidth;
- dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));
+ dib7000p_set_output_mode(state, OUTMODE_HIGH_Z);
- /* maybe the parameter has been changed */
+ /* maybe the parameter has been changed */
state->sfn_workaround_active = buggy_sfn_workaround;
if (fe->ops.tuner_ops.set_params)
@@ -1166,10 +1165,11 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe,
dib7000p_get_frontend(fe, fep);
}
+ ret = dib7000p_tune(fe, fep);
+
/* make this a config parameter */
dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO);
-
- return dib7000p_tune(fe, fep);
+ return ret;
}
static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
diff --git a/trunk/drivers/media/dvb/frontends/dibx000_common.h b/trunk/drivers/media/dvb/frontends/dibx000_common.h
index 5e17275afd25..84e4d5362922 100644
--- a/trunk/drivers/media/dvb/frontends/dibx000_common.h
+++ b/trunk/drivers/media/dvb/frontends/dibx000_common.h
@@ -128,6 +128,11 @@ enum dibx000_adc_states {
(v) == BANDWIDTH_7_MHZ ? 7000 : \
(v) == BANDWIDTH_6_MHZ ? 6000 : 8000 )
+#define BANDWIDTH_TO_INDEX(v) ( \
+ (v) == 8000 ? BANDWIDTH_8_MHZ : \
+ (v) == 7000 ? BANDWIDTH_7_MHZ : \
+ (v) == 6000 ? BANDWIDTH_6_MHZ : BANDWIDTH_8_MHZ )
+
/* Chip output mode. */
#define OUTMODE_HIGH_Z 0
#define OUTMODE_MPEG2_PAR_GATED_CLK 1
diff --git a/trunk/drivers/media/dvb/frontends/mt2266.c b/trunk/drivers/media/dvb/frontends/mt2266.c
index 03fe8265745f..54b18f94b14b 100644
--- a/trunk/drivers/media/dvb/frontends/mt2266.c
+++ b/trunk/drivers/media/dvb/frontends/mt2266.c
@@ -38,8 +38,12 @@ struct mt2266_priv {
u32 frequency;
u32 bandwidth;
+ u8 band;
};
+#define MT2266_VHF 1
+#define MT2266_UHF 0
+
/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
static int debug;
@@ -90,26 +94,30 @@ static int mt2266_writeregs(struct mt2266_priv *priv,u8 *buf, u8 len)
}
// Initialisation sequences
-static u8 mt2266_init1[] = {
- REG_TUNE,
- 0x00, 0x00, 0x28, 0x00, 0x52, 0x99, 0x3f };
+static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28,
+ 0x00, 0x52, 0x99, 0x3f };
static u8 mt2266_init2[] = {
- 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a,
- 0xd4, 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x77, 0x0f, 0x2d };
+ 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, 0xd4,
+ 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff,
+ 0xff, 0x00, 0x77, 0x0f, 0x2d
+};
+
+static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22 };
-static u8 mt2266_init_8mhz[] = {
- REG_BANDWIDTH,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 };
+static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32 };
-static u8 mt2266_init_7mhz[] = {
- REG_BANDWIDTH,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 };
+static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7 };
-static u8 mt2266_init_6mhz[] = {
- REG_BANDWIDTH,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7 };
+static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64,
+ 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 };
+
+static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f };
#define FREF 30000 // Quartz oscillator 30 MHz
@@ -122,35 +130,78 @@ static int mt2266_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
u8 lnaband;
u8 b[10];
int i;
+ u8 band;
priv = fe->tuner_priv;
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0xff);
-
freq = params->frequency / 1000; // Hz -> kHz
+ if (freq < 470000 && freq > 230000)
+ return -EINVAL; /* Gap between VHF and UHF bands */
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
priv->frequency = freq * 1000;
- tune=2 * freq * (8192/16) / (FREF/16);
-
- if (freq <= 495000) lnaband = 0xEE; else
- if (freq <= 525000) lnaband = 0xDD; else
- if (freq <= 550000) lnaband = 0xCC; else
- if (freq <= 580000) lnaband = 0xBB; else
- if (freq <= 605000) lnaband = 0xAA; else
- if (freq <= 630000) lnaband = 0x99; else
- if (freq <= 655000) lnaband = 0x88; else
- if (freq <= 685000) lnaband = 0x77; else
- if (freq <= 710000) lnaband = 0x66; else
- if (freq <= 735000) lnaband = 0x55; else
- if (freq <= 765000) lnaband = 0x44; else
- if (freq <= 802000) lnaband = 0x33; else
- if (freq <= 840000) lnaband = 0x22; else lnaband = 0x11;
-
- msleep(100);
- mt2266_writeregs(priv,(params->u.ofdm.bandwidth==BANDWIDTH_6_MHZ)?mt2266_init_6mhz:
- (params->u.ofdm.bandwidth==BANDWIDTH_7_MHZ)?mt2266_init_7mhz:
- mt2266_init_8mhz,sizeof(mt2266_init_8mhz));
+
+ tune = 2 * freq * (8192/16) / (FREF/16);
+ band = (freq < 300000) ? MT2266_VHF : MT2266_UHF;
+ if (band == MT2266_VHF)
+ tune *= 2;
+
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ mt2266_writeregs(priv, mt2266_init_6mhz,
+ sizeof(mt2266_init_6mhz));
+ break;
+ case BANDWIDTH_7_MHZ:
+ mt2266_writeregs(priv, mt2266_init_7mhz,
+ sizeof(mt2266_init_7mhz));
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ mt2266_writeregs(priv, mt2266_init_8mhz,
+ sizeof(mt2266_init_8mhz));
+ break;
+ }
+
+ if (band == MT2266_VHF && priv->band == MT2266_UHF) {
+ dprintk("Switch from UHF to VHF");
+ mt2266_writereg(priv, 0x05, 0x04);
+ mt2266_writereg(priv, 0x19, 0x61);
+ mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf));
+ } else if (band == MT2266_UHF && priv->band == MT2266_VHF) {
+ dprintk("Switch from VHF to UHF");
+ mt2266_writereg(priv, 0x05, 0x52);
+ mt2266_writereg(priv, 0x19, 0x61);
+ mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf));
+ }
+ msleep(10);
+
+ if (freq <= 495000)
+ lnaband = 0xEE;
+ else if (freq <= 525000)
+ lnaband = 0xDD;
+ else if (freq <= 550000)
+ lnaband = 0xCC;
+ else if (freq <= 580000)
+ lnaband = 0xBB;
+ else if (freq <= 605000)
+ lnaband = 0xAA;
+ else if (freq <= 630000)
+ lnaband = 0x99;
+ else if (freq <= 655000)
+ lnaband = 0x88;
+ else if (freq <= 685000)
+ lnaband = 0x77;
+ else if (freq <= 710000)
+ lnaband = 0x66;
+ else if (freq <= 735000)
+ lnaband = 0x55;
+ else if (freq <= 765000)
+ lnaband = 0x44;
+ else if (freq <= 802000)
+ lnaband = 0x33;
+ else if (freq <= 840000)
+ lnaband = 0x22;
+ else
+ lnaband = 0x11;
b[0] = REG_TUNE;
b[1] = (tune >> 8) & 0x1F;
@@ -158,47 +209,54 @@ static int mt2266_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
b[3] = tune >> 13;
mt2266_writeregs(priv,b,4);
- dprintk("set_parms: tune=%d band=%d",(int)tune,(int)lnaband);
- dprintk("set_parms: [1..3]: %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3]);
-
- b[0] = 0x05;
- b[1] = 0x62;
- b[2] = lnaband;
- mt2266_writeregs(priv,b,3);
+ dprintk("set_parms: tune=%d band=%d %s",
+ (int) tune, (int) lnaband,
+ (band == MT2266_UHF) ? "UHF" : "VHF");
+ dprintk("set_parms: [1..3]: %2x %2x %2x",
+ (int) b[1], (int) b[2], (int)b[3]);
+
+ if (band == MT2266_UHF) {
+ b[0] = 0x05;
+ b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62;
+ b[2] = lnaband;
+ mt2266_writeregs(priv, b, 3);
+ }
- //Waits for pll lock or timeout
+ /* Wait for pll lock or timeout */
i = 0;
do {
mt2266_readreg(priv,REG_LOCK,b);
- if ((b[0] & 0x40)==0x40)
+ if (b[0] & 0x40)
break;
msleep(10);
i++;
} while (i<10);
dprintk("Lock when i=%i",(int)i);
+
+ if (band == MT2266_UHF && priv->band == MT2266_VHF)
+ mt2266_writereg(priv, 0x05, 0x62);
+
+ priv->band = band;
+
return ret;
}
static void mt2266_calibrate(struct mt2266_priv *priv)
{
- mt2266_writereg(priv,0x11,0x03);
- mt2266_writereg(priv,0x11,0x01);
-
- mt2266_writeregs(priv,mt2266_init1,sizeof(mt2266_init1));
- mt2266_writeregs(priv,mt2266_init2,sizeof(mt2266_init2));
-
- mt2266_writereg(priv,0x33,0x5e);
- mt2266_writereg(priv,0x10,0x10);
- mt2266_writereg(priv,0x10,0x00);
-
- mt2266_writeregs(priv,mt2266_init_8mhz,sizeof(mt2266_init_8mhz));
-
+ mt2266_writereg(priv, 0x11, 0x03);
+ mt2266_writereg(priv, 0x11, 0x01);
+ mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1));
+ mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2));
+ mt2266_writereg(priv, 0x33, 0x5e);
+ mt2266_writereg(priv, 0x10, 0x10);
+ mt2266_writereg(priv, 0x10, 0x00);
+ mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz));
msleep(25);
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0x00);
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0x00);
msleep(75);
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0xff);
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0xff);
}
static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency)
@@ -217,17 +275,22 @@ static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
static int mt2266_init(struct dvb_frontend *fe)
{
+ int ret;
struct mt2266_priv *priv = fe->tuner_priv;
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0xff);
+ ret = mt2266_writereg(priv, 0x17, 0x6d);
+ if (ret < 0)
+ return ret;
+ ret = mt2266_writereg(priv, 0x1c, 0xff);
+ if (ret < 0)
+ return ret;
return 0;
}
static int mt2266_sleep(struct dvb_frontend *fe)
{
struct mt2266_priv *priv = fe->tuner_priv;
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0x00);
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0x00);
return 0;
}
@@ -241,8 +304,8 @@ static int mt2266_release(struct dvb_frontend *fe)
static const struct dvb_tuner_ops mt2266_tuner_ops = {
.info = {
.name = "Microtune MT2266",
- .frequency_min = 470000000,
- .frequency_max = 860000000,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
.frequency_step = 50000,
},
.release = mt2266_release,
@@ -264,8 +327,9 @@ struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter
priv->cfg = cfg;
priv->i2c = i2c;
+ priv->band = MT2266_UHF;
- if (mt2266_readreg(priv,0,&id) != 0) {
+ if (mt2266_readreg(priv, 0, &id)) {
kfree(priv);
return NULL;
}
diff --git a/trunk/drivers/media/dvb/frontends/mt312.c b/trunk/drivers/media/dvb/frontends/mt312.c
index 0606b9a5b616..1638301fbd6e 100644
--- a/trunk/drivers/media/dvb/frontends/mt312.c
+++ b/trunk/drivers/media/dvb/frontends/mt312.c
@@ -37,9 +37,9 @@
struct mt312_state {
- struct i2c_adapter* i2c;
+ struct i2c_adapter *i2c;
/* configuration settings */
- const struct mt312_config* config;
+ const struct mt312_config *config;
struct dvb_frontend frontend;
u8 id;
@@ -49,14 +49,15 @@ struct mt312_state {
static int debug;
#define dprintk(args...) \
do { \
- if (debug) printk(KERN_DEBUG "mt312: " args); \
+ if (debug) \
+ printk(KERN_DEBUG "mt312: " args); \
} while (0)
#define MT312_SYS_CLK 90000000UL /* 90 MHz */
#define MT312_LPOWER_SYS_CLK 60000000UL /* 60 MHz */
#define MT312_PLL_CLK 10000000UL /* 10 MHz */
-static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
+static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg,
void *buf, const size_t count)
{
int ret;
@@ -79,7 +80,7 @@ static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
return -EREMOTEIO;
}
- if(debug) {
+ if (debug) {
int i;
dprintk("R(%d):", reg & 0x7f);
for (i = 0; i < count; i++)
@@ -90,14 +91,14 @@ static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
return 0;
}
-static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg,
+static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg,
const void *src, const size_t count)
{
int ret;
u8 buf[count + 1];
struct i2c_msg msg;
- if(debug) {
+ if (debug) {
int i;
dprintk("W(%d):", reg & 0x7f);
for (i = 0; i < count; i++)
@@ -123,13 +124,13 @@ static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg,
return 0;
}
-static inline int mt312_readreg(struct mt312_state* state,
+static inline int mt312_readreg(struct mt312_state *state,
const enum mt312_reg_addr reg, u8 *val)
{
return mt312_read(state, reg, val, 1);
}
-static inline int mt312_writereg(struct mt312_state* state,
+static inline int mt312_writereg(struct mt312_state *state,
const enum mt312_reg_addr reg, const u8 val)
{
return mt312_write(state, reg, &val, 1);
@@ -140,18 +141,19 @@ static inline u32 mt312_div(u32 a, u32 b)
return (a + (b / 2)) / b;
}
-static int mt312_reset(struct mt312_state* state, const u8 full)
+static int mt312_reset(struct mt312_state *state, const u8 full)
{
return mt312_writereg(state, RESET, full ? 0x80 : 0x40);
}
-static int mt312_get_inversion(struct mt312_state* state,
+static int mt312_get_inversion(struct mt312_state *state,
fe_spectral_inversion_t *i)
{
int ret;
u8 vit_mode;
- if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0)
+ ret = mt312_readreg(state, VIT_MODE, &vit_mode);
+ if (ret < 0)
return ret;
if (vit_mode & 0x80) /* auto inversion was used */
@@ -160,7 +162,7 @@ static int mt312_get_inversion(struct mt312_state* state,
return 0;
}
-static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
+static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr)
{
int ret;
u8 sym_rate_h;
@@ -169,37 +171,44 @@ static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
u16 monitor;
u8 buf[2];
- if ((ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h)) < 0)
+ ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h);
+ if (ret < 0)
return ret;
- if (sym_rate_h & 0x80) { /* symbol rate search was used */
- if ((ret = mt312_writereg(state, MON_CTRL, 0x03)) < 0)
+ if (sym_rate_h & 0x80) {
+ /* symbol rate search was used */
+ ret = mt312_writereg(state, MON_CTRL, 0x03);
+ if (ret < 0)
return ret;
- if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, MONITOR_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
monitor = (buf[0] << 8) | buf[1];
- dprintk(KERN_DEBUG "sr(auto) = %u\n",
+ dprintk("sr(auto) = %u\n",
mt312_div(monitor * 15625, 4));
} else {
- if ((ret = mt312_writereg(state, MON_CTRL, 0x05)) < 0)
+ ret = mt312_writereg(state, MON_CTRL, 0x05);
+ if (ret < 0)
return ret;
- if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, MONITOR_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
dec_ratio = ((buf[0] >> 5) & 0x07) * 32;
- if ((ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
sym_rat_op = (buf[0] << 8) | buf[1];
- dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n",
+ dprintk("sym_rat_op=%d dec_ratio=%d\n",
sym_rat_op, dec_ratio);
- dprintk(KERN_DEBUG "*sr(manual) = %lu\n",
+ dprintk("*sr(manual) = %lu\n",
(((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) *
2) - dec_ratio);
}
@@ -207,7 +216,7 @@ static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
return 0;
}
-static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
+static int mt312_get_code_rate(struct mt312_state *state, fe_code_rate_t *cr)
{
const fe_code_rate_t fec_tab[8] =
{ FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8,
@@ -216,7 +225,8 @@ static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
int ret;
u8 fec_status;
- if ((ret = mt312_readreg(state, FEC_STATUS, &fec_status)) < 0)
+ ret = mt312_readreg(state, FEC_STATUS, &fec_status);
+ if (ret < 0)
return ret;
*cr = fec_tab[(fec_status >> 4) & 0x07];
@@ -224,61 +234,72 @@ static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
return 0;
}
-static int mt312_initfe(struct dvb_frontend* fe)
+static int mt312_initfe(struct dvb_frontend *fe)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 buf[2];
/* wake up */
- if ((ret = mt312_writereg(state, CONFIG, (state->frequency == 60 ? 0x88 : 0x8c))) < 0)
+ ret = mt312_writereg(state, CONFIG,
+ (state->frequency == 60 ? 0x88 : 0x8c));
+ if (ret < 0)
return ret;
/* wait at least 150 usec */
udelay(150);
/* full reset */
- if ((ret = mt312_reset(state, 1)) < 0)
+ ret = mt312_reset(state, 1);
+ if (ret < 0)
return ret;
-// Per datasheet, write correct values. 09/28/03 ACCJr.
-// If we don't do this, we won't get FE_HAS_VITERBI in the VP310.
+/* Per datasheet, write correct values. 09/28/03 ACCJr.
+ * If we don't do this, we won't get FE_HAS_VITERBI in the VP310. */
{
- u8 buf_def[8]={0x14, 0x12, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00};
+ u8 buf_def[8] = { 0x14, 0x12, 0x03, 0x02,
+ 0x01, 0x00, 0x00, 0x00 };
- if ((ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def))) < 0)
+ ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def));
+ if (ret < 0)
return ret;
}
/* SYS_CLK */
- buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000);
+ buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK :
+ MT312_SYS_CLK) * 2, 1000000);
/* DISEQC_RATIO */
buf[1] = mt312_div(MT312_PLL_CLK, 15000 * 4);
- if ((ret = mt312_write(state, SYS_CLK, buf, sizeof(buf))) < 0)
+ ret = mt312_write(state, SYS_CLK, buf, sizeof(buf));
+ if (ret < 0)
return ret;
- if ((ret = mt312_writereg(state, SNR_THS_HIGH, 0x32)) < 0)
+ ret = mt312_writereg(state, SNR_THS_HIGH, 0x32);
+ if (ret < 0)
return ret;
- if ((ret = mt312_writereg(state, OP_CTRL, 0x53)) < 0)
+ ret = mt312_writereg(state, OP_CTRL, 0x53);
+ if (ret < 0)
return ret;
/* TS_SW_LIM */
buf[0] = 0x8c;
buf[1] = 0x98;
- if ((ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf))) < 0)
+ ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf));
+ if (ret < 0)
return ret;
- if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0)
+ ret = mt312_writereg(state, CS_SW_LIM, 0x69);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_send_master_cmd(struct dvb_frontend* fe,
+static int mt312_send_master_cmd(struct dvb_frontend *fe,
struct dvb_diseqc_master_cmd *c)
{
struct mt312_state *state = fe->demodulator_priv;
@@ -288,29 +309,31 @@ static int mt312_send_master_cmd(struct dvb_frontend* fe,
if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg)))
return -EINVAL;
- if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode);
+ if (ret < 0)
return ret;
- if ((ret =
- mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0)
+ ret = mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len);
+ if (ret < 0)
return ret;
- if ((ret =
- mt312_writereg(state, DISEQC_MODE,
- (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3)
- | 0x04)) < 0)
+ ret = mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3)
+ | 0x04);
+ if (ret < 0)
return ret;
/* set DISEQC_MODE[2:0] to zero if a return message is expected */
- if (c->msg[0] & 0x02)
- if ((ret =
- mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40))) < 0)
+ if (c->msg[0] & 0x02) {
+ ret = mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40));
+ if (ret < 0)
return ret;
+ }
return 0;
}
-static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c)
+static int mt312_send_burst(struct dvb_frontend *fe, const fe_sec_mini_cmd_t c)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 mini_tab[2] = { 0x02, 0x03 };
@@ -321,18 +344,19 @@ static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c)
if (c > SEC_MINI_B)
return -EINVAL;
- if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode);
+ if (ret < 0)
return ret;
- if ((ret =
- mt312_writereg(state, DISEQC_MODE,
- (diseqc_mode & 0x40) | mini_tab[c])) < 0)
+ ret = mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | mini_tab[c]);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t)
+static int mt312_set_tone(struct dvb_frontend *fe, const fe_sec_tone_mode_t t)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 tone_tab[2] = { 0x01, 0x00 };
@@ -343,18 +367,19 @@ static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t)
if (t > SEC_TONE_OFF)
return -EINVAL;
- if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode);
+ if (ret < 0)
return ret;
- if ((ret =
- mt312_writereg(state, DISEQC_MODE,
- (diseqc_mode & 0x40) | tone_tab[t])) < 0)
+ ret = mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | tone_tab[t]);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v)
+static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 volt_tab[3] = { 0x00, 0x40, 0x00 };
@@ -365,7 +390,7 @@ static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v)
return mt312_writereg(state, DISEQC_MODE, volt_tab[v]);
}
-static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
+static int mt312_read_status(struct dvb_frontend *fe, fe_status_t *s)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
@@ -373,10 +398,12 @@ static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
*s = 0;
- if ((ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status))) < 0)
+ ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status));
+ if (ret < 0)
return ret;
- dprintk(KERN_DEBUG "QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]);
+ dprintk("QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x,"
+ " FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]);
if (status[0] & 0xc0)
*s |= FE_HAS_SIGNAL; /* signal noise ratio */
@@ -392,13 +419,14 @@ static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
return 0;
}
-static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber)
+static int mt312_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 buf[3];
- if ((ret = mt312_read(state, RS_BERCNT_H, buf, 3)) < 0)
+ ret = mt312_read(state, RS_BERCNT_H, buf, 3);
+ if (ret < 0)
return ret;
*ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64;
@@ -406,7 +434,8 @@ static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber)
return 0;
}
-static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_strength)
+static int mt312_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
@@ -414,7 +443,8 @@ static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_stren
u16 agc;
s16 err_db;
- if ((ret = mt312_read(state, AGC_H, buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, AGC_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
agc = (buf[0] << 6) | (buf[1] >> 2);
@@ -422,18 +452,19 @@ static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_stren
*signal_strength = agc;
- dprintk(KERN_DEBUG "agc=%08x err_db=%hd\n", agc, err_db);
+ dprintk("agc=%08x err_db=%hd\n", agc, err_db);
return 0;
}
-static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr)
+static int mt312_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 buf[2];
- if ((ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf));
+ if (ret < 0)
return ret;
*snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1);
@@ -441,13 +472,14 @@ static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr)
return 0;
}
-static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc)
+static int mt312_read_ucblocks(struct dvb_frontend *fe, u32 *ubc)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 buf[2];
- if ((ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf));
+ if (ret < 0)
return ret;
*ubc = (buf[0] << 8) | buf[1];
@@ -455,7 +487,7 @@ static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc)
return 0;
}
-static int mt312_set_frontend(struct dvb_frontend* fe,
+static int mt312_set_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p)
{
struct mt312_state *state = fe->demodulator_priv;
@@ -491,24 +523,28 @@ static int mt312_set_frontend(struct dvb_frontend* fe,
switch (state->id) {
case ID_VP310:
- // For now we will do this only for the VP310.
- // It should be better for the mt312 as well, but tunning will be slower. ACCJr 09/29/03
+ /* For now we will do this only for the VP310.
+ * It should be better for the mt312 as well,
+ * but tuning will be slower. ACCJr 09/29/03
+ */
ret = mt312_readreg(state, CONFIG, &config_val);
if (ret < 0)
return ret;
- if (p->u.qpsk.symbol_rate >= 30000000) //Note that 30MS/s should use 90MHz
- {
- if ((config_val & 0x0c) == 0x08) { //We are running 60MHz
+ if (p->u.qpsk.symbol_rate >= 30000000) {
+ /* Note that 30MS/s should use 90MHz */
+ if ((config_val & 0x0c) == 0x08) {
+ /* We are running 60MHz */
state->frequency = 90;
- if ((ret = mt312_initfe(fe)) < 0)
+ ret = mt312_initfe(fe);
+ if (ret < 0)
return ret;
}
- }
- else
- {
- if ((config_val & 0x0c) == 0x0C) { //We are running 90MHz
+ } else {
+ if ((config_val & 0x0c) == 0x0C) {
+ /* We are running 90MHz */
state->frequency = 60;
- if ((ret = mt312_initfe(fe)) < 0)
+ ret = mt312_initfe(fe);
+ if (ret < 0)
return ret;
}
}
@@ -523,7 +559,8 @@ static int mt312_set_frontend(struct dvb_frontend* fe,
if (fe->ops.tuner_ops.set_params) {
fe->ops.tuner_ops.set_params(fe, p);
- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
}
/* sr = (u16)(sr * 256.0 / 1000000.0) */
@@ -545,7 +582,8 @@ static int mt312_set_frontend(struct dvb_frontend* fe,
/* GO */
buf[4] = 0x01;
- if ((ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf))) < 0)
+ ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
mt312_reset(state, 0);
@@ -553,27 +591,30 @@ static int mt312_set_frontend(struct dvb_frontend* fe,
return 0;
}
-static int mt312_get_frontend(struct dvb_frontend* fe,
+static int mt312_get_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
- if ((ret = mt312_get_inversion(state, &p->inversion)) < 0)
+ ret = mt312_get_inversion(state, &p->inversion);
+ if (ret < 0)
return ret;
- if ((ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate)) < 0)
+ ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate);
+ if (ret < 0)
return ret;
- if ((ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner)) < 0)
+ ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+static int mt312_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
- struct mt312_state* state = fe->demodulator_priv;
+ struct mt312_state *state = fe->demodulator_priv;
if (enable) {
return mt312_writereg(state, GPP_CTRL, 0x40);
@@ -582,27 +623,31 @@ static int mt312_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
}
}
-static int mt312_sleep(struct dvb_frontend* fe)
+static int mt312_sleep(struct dvb_frontend *fe)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 config;
/* reset all registers to defaults */
- if ((ret = mt312_reset(state, 1)) < 0)
+ ret = mt312_reset(state, 1);
+ if (ret < 0)
return ret;
- if ((ret = mt312_readreg(state, CONFIG, &config)) < 0)
+ ret = mt312_readreg(state, CONFIG, &config);
+ if (ret < 0)
return ret;
/* enter standby */
- if ((ret = mt312_writereg(state, CONFIG, config & 0x7f)) < 0)
+ ret = mt312_writereg(state, CONFIG, config & 0x7f);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+static int mt312_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
{
fesettings->min_delay_ms = 50;
fesettings->step_size = 0;
@@ -610,9 +655,9 @@ static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_
return 0;
}
-static void mt312_release(struct dvb_frontend* fe)
+static void mt312_release(struct dvb_frontend *fe)
{
- struct mt312_state* state = fe->demodulator_priv;
+ struct mt312_state *state = fe->demodulator_priv;
kfree(state);
}
@@ -655,10 +700,10 @@ static struct dvb_frontend_ops vp310_mt312_ops = {
.set_voltage = mt312_set_voltage,
};
-struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
- struct i2c_adapter* i2c)
+struct dvb_frontend *vp310_mt312_attach(const struct mt312_config *config,
+ struct i2c_adapter *i2c)
{
- struct mt312_state* state = NULL;
+ struct mt312_state *state = NULL;
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
@@ -674,7 +719,8 @@ struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
goto error;
/* create dvb_frontend */
- memcpy(&state->frontend.ops, &vp310_mt312_ops, sizeof(struct dvb_frontend_ops));
+ memcpy(&state->frontend.ops, &vp310_mt312_ops,
+ sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
switch (state->id) {
@@ -687,7 +733,8 @@ struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
state->frequency = 60;
break;
default:
- printk (KERN_WARNING "Only Zarlink VP310/MT312 are supported chips.\n");
+ printk(KERN_WARNING "Only Zarlink VP310/MT312"
+ " are supported chips.\n");
goto error;
}
@@ -697,6 +744,7 @@ struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
kfree(state);
return NULL;
}
+EXPORT_SYMBOL(vp310_mt312_attach);
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
@@ -705,4 +753,3 @@ MODULE_DESCRIPTION("Zarlink VP310/MT312 DVB-S Demodulator driver");
MODULE_AUTHOR("Andreas Oberritter ");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(vp310_mt312_attach);
diff --git a/trunk/drivers/media/dvb/frontends/mt312.h b/trunk/drivers/media/dvb/frontends/mt312.h
index cf9a1505ad4b..f17cb93ba9ba 100644
--- a/trunk/drivers/media/dvb/frontends/mt312.h
+++ b/trunk/drivers/media/dvb/frontends/mt312.h
@@ -28,22 +28,21 @@
#include
-struct mt312_config
-{
+struct mt312_config {
/* the demodulator's i2c address */
u8 demod_address;
};
#if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE))
-struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
- struct i2c_adapter* i2c);
+struct dvb_frontend *vp310_mt312_attach(const struct mt312_config *config,
+ struct i2c_adapter *i2c);
#else
-static inline struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
- struct i2c_adapter* i2c)
+static inline struct dvb_frontend *vp310_mt312_attach(
+ const struct mt312_config *config, struct i2c_adapter *i2c)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
return NULL;
}
-#endif // CONFIG_DVB_MT312
+#endif /* CONFIG_DVB_MT312 */
-#endif // MT312_H
+#endif /* MT312_H */
diff --git a/trunk/drivers/media/dvb/frontends/mt352.c b/trunk/drivers/media/dvb/frontends/mt352.c
index 5dd9b731f6f2..7cd190b6f015 100644
--- a/trunk/drivers/media/dvb/frontends/mt352.c
+++ b/trunk/drivers/media/dvb/frontends/mt352.c
@@ -152,7 +152,13 @@ static void mt352_calc_input_freq(struct mt352_state* state,
if (state->config.if2)
if2 = state->config.if2;
- ife = (2*adc_clock - if2);
+ if (adc_clock >= if2 * 2)
+ ife = if2;
+ else {
+ ife = adc_clock - (if2 % adc_clock);
+ if (ife > adc_clock / 2)
+ ife = adc_clock - ife;
+ }
value = -16374 * ife / adc_clock;
dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n",
__FUNCTION__, if2, ife, adc_clock, value, value & 0x3fff);
diff --git a/trunk/drivers/media/dvb/frontends/or51132.c b/trunk/drivers/media/dvb/frontends/or51132.c
index b314a1f2deed..1d2d28ce823d 100644
--- a/trunk/drivers/media/dvb/frontends/or51132.c
+++ b/trunk/drivers/media/dvb/frontends/or51132.c
@@ -564,7 +564,7 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config,
/* Allocate memory for the internal state */
state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL);
if (state == NULL)
- goto error;
+ return NULL;
/* Setup the state */
state->config = config;
@@ -576,10 +576,6 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config,
memcpy(&state->frontend.ops, &or51132_ops, sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
-
-error:
- kfree(state);
- return NULL;
}
static struct dvb_frontend_ops or51132_ops = {
diff --git a/trunk/drivers/media/dvb/frontends/or51211.c b/trunk/drivers/media/dvb/frontends/or51211.c
index f02bd9445955..6a6b0d727c6f 100644
--- a/trunk/drivers/media/dvb/frontends/or51211.c
+++ b/trunk/drivers/media/dvb/frontends/or51211.c
@@ -529,7 +529,7 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config,
/* Allocate memory for the internal state */
state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL);
if (state == NULL)
- goto error;
+ return NULL;
/* Setup the state */
state->config = config;
@@ -541,10 +541,6 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config,
memcpy(&state->frontend.ops, &or51211_ops, sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
-
-error:
- kfree(state);
- return NULL;
}
static struct dvb_frontend_ops or51211_ops = {
diff --git a/trunk/drivers/media/dvb/frontends/s5h1409.c b/trunk/drivers/media/dvb/frontends/s5h1409.c
index 562d9208857a..819433485d3b 100644
--- a/trunk/drivers/media/dvb/frontends/s5h1409.c
+++ b/trunk/drivers/media/dvb/frontends/s5h1409.c
@@ -42,6 +42,7 @@ struct s5h1409_state {
fe_modulation_t current_modulation;
u32 current_frequency;
+ int if_freq;
u32 is_qam_locked;
u32 qam_state;
@@ -97,7 +98,7 @@ static struct init_tab {
{ 0xac, 0x1003, },
{ 0xad, 0x103f, },
{ 0xe2, 0x0100, },
- { 0xe3, 0x0000, },
+ { 0xe3, 0x1000, },
{ 0x28, 0x1010, },
{ 0xb1, 0x000e, },
};
@@ -348,28 +349,32 @@ static int s5h1409_softreset(struct dvb_frontend* fe)
return 0;
}
+#define S5H1409_VSB_IF_FREQ 5380
+#define S5H1409_QAM_IF_FREQ state->config->qam_if
+
static int s5h1409_set_if_freq(struct dvb_frontend* fe, int KHz)
{
struct s5h1409_state* state = fe->demodulator_priv;
- int ret = 0;
dprintk("%s(%d KHz)\n", __FUNCTION__, KHz);
- if( (KHz == 44000) || (KHz == 5380) ) {
- s5h1409_writereg(state, 0x87, 0x01be);
- s5h1409_writereg(state, 0x88, 0x0436);
- s5h1409_writereg(state, 0x89, 0x054d);
- } else
- if (KHz == 4000) {
+ switch (KHz) {
+ case 4000:
s5h1409_writereg(state, 0x87, 0x014b);
s5h1409_writereg(state, 0x88, 0x0cb5);
s5h1409_writereg(state, 0x89, 0x03e2);
- } else {
- printk("%s() Invalid arg = %d KHz\n", __FUNCTION__, KHz);
- ret = -1;
+ break;
+ case 5380:
+ case 44000:
+ default:
+ s5h1409_writereg(state, 0x87, 0x01be);
+ s5h1409_writereg(state, 0x88, 0x0436);
+ s5h1409_writereg(state, 0x89, 0x054d);
+ break;
}
+ state->if_freq = KHz;
- return ret;
+ return 0;
}
static int s5h1409_set_spectralinversion(struct dvb_frontend* fe, int inverted)
@@ -394,11 +399,15 @@ static int s5h1409_enable_modulation(struct dvb_frontend* fe,
switch(m) {
case VSB_8:
dprintk("%s() VSB_8\n", __FUNCTION__);
+ if (state->if_freq != S5H1409_VSB_IF_FREQ)
+ s5h1409_set_if_freq(fe, S5H1409_VSB_IF_FREQ);
s5h1409_writereg(state, 0xf4, 0);
break;
case QAM_64:
case QAM_256:
dprintk("%s() QAM_AUTO (64/256)\n", __FUNCTION__);
+ if (state->if_freq != S5H1409_QAM_IF_FREQ)
+ s5h1409_set_if_freq(fe, S5H1409_QAM_IF_FREQ);
s5h1409_writereg(state, 0xf4, 1);
s5h1409_writereg(state, 0x85, 0x110);
break;
@@ -432,9 +441,11 @@ static int s5h1409_set_gpio(struct dvb_frontend* fe, int enable)
dprintk("%s(%d)\n", __FUNCTION__, enable);
if (enable)
- return s5h1409_writereg(state, 0xe3, 0x1100);
+ return s5h1409_writereg(state, 0xe3,
+ s5h1409_readreg(state, 0xe3) | 0x1100);
else
- return s5h1409_writereg(state, 0xe3, 0x1000);
+ return s5h1409_writereg(state, 0xe3,
+ s5h1409_readreg(state, 0xe3) & 0xeeff);
}
static int s5h1409_sleep(struct dvb_frontend* fe, int enable)
@@ -504,13 +515,15 @@ static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe)
s5h1409_writereg(state, 0x96, 0x20);
s5h1409_writereg(state, 0xad,
( ((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)) );
- s5h1409_writereg(state, 0xab, 0x1100);
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) & 0xeffe);
}
} else {
if (state->qam_state != 1) {
state->qam_state = 1;
s5h1409_writereg(state, 0x96, 0x08);
- s5h1409_writereg(state, 0xab, 0x1101);
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) | 0x1001);
}
}
}
@@ -547,6 +560,36 @@ static int s5h1409_set_frontend (struct dvb_frontend* fe,
return 0;
}
+static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode)
+{
+ struct s5h1409_state *state = fe->demodulator_priv;
+ u16 val;
+
+ dprintk("%s(%d)\n", __FUNCTION__, mode);
+
+ val = s5h1409_readreg(state, 0xac) & 0xcfff;
+ switch (mode) {
+ case S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK:
+ val |= 0x0000;
+ break;
+ case S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK:
+ dprintk("%s(%d) Mode1 or Defaulting\n", __FUNCTION__, mode);
+ val |= 0x1000;
+ break;
+ case S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK:
+ val |= 0x2000;
+ break;
+ case S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK:
+ val |= 0x3000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Configure MPEG Signal Timing charactistics */
+ return s5h1409_writereg(state, 0xac, val);
+}
+
/* Reset the demod hardware and reset all of the configuration registers
to a default state. */
static int s5h1409_init (struct dvb_frontend* fe)
@@ -566,13 +609,16 @@ static int s5h1409_init (struct dvb_frontend* fe)
state->current_modulation = VSB_8;
if (state->config->output_mode == S5H1409_SERIAL_OUTPUT)
- s5h1409_writereg(state, 0xab, 0x100); /* Serial */
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) | 0x100); /* Serial */
else
- s5h1409_writereg(state, 0xab, 0x0); /* Parallel */
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) & 0xfeff); /* Parallel */
s5h1409_set_spectralinversion(fe, state->config->inversion);
- s5h1409_set_if_freq(fe, state->config->if_freq);
+ s5h1409_set_if_freq(fe, state->if_freq);
s5h1409_set_gpio(fe, state->config->gpio);
+ s5h1409_set_mpeg_timing(fe, state->config->mpeg_timing);
s5h1409_softreset(fe);
/* Note: Leaving the I2C gate closed. */
@@ -741,6 +787,7 @@ struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config,
struct i2c_adapter* i2c)
{
struct s5h1409_state* state = NULL;
+ u16 reg;
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
@@ -751,9 +798,11 @@ struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config,
state->config = config;
state->i2c = i2c;
state->current_modulation = 0;
+ state->if_freq = S5H1409_VSB_IF_FREQ;
/* check if the demod exists */
- if (s5h1409_readreg(state, 0x04) != 0x0066)
+ reg = s5h1409_readreg(state, 0x04);
+ if ((reg != 0x0066) && (reg != 0x007f))
goto error;
/* create dvb_frontend */
@@ -761,8 +810,14 @@ struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config,
sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
+ if (s5h1409_init(&state->frontend) != 0) {
+ printk(KERN_ERR "%s: Failed to initialize correctly\n",
+ __FUNCTION__);
+ goto error;
+ }
+
/* Note: Leaving the I2C gate open here. */
- s5h1409_writereg(state, 0xf3, 1);
+ s5h1409_i2c_gate_ctrl(&state->frontend, 1);
return &state->frontend;
diff --git a/trunk/drivers/media/dvb/frontends/s5h1409.h b/trunk/drivers/media/dvb/frontends/s5h1409.h
index 20f9af1af445..f0bb13fe808b 100644
--- a/trunk/drivers/media/dvb/frontends/s5h1409.h
+++ b/trunk/drivers/media/dvb/frontends/s5h1409.h
@@ -39,8 +39,8 @@ struct s5h1409_config
#define S5H1409_GPIO_ON 1
u8 gpio;
- /* IF Freq in KHz */
- u16 if_freq;
+ /* IF Freq for QAM in KHz, VSB is hardcoded to 5380 */
+ u16 qam_if;
/* Spectral Inversion */
#define S5H1409_INVERSION_OFF 0
@@ -51,6 +51,13 @@ struct s5h1409_config
#define S5H1409_TUNERLOCKING 0
#define S5H1409_DEMODLOCKING 1
u8 status_mode;
+
+ /* MPEG signal timing */
+#define S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0
+#define S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1
+#define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2
+#define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3
+ u16 mpeg_timing;
};
#if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) && defined(MODULE))
diff --git a/trunk/drivers/media/dvb/frontends/tda18271-common.c b/trunk/drivers/media/dvb/frontends/tda18271-common.c
new file mode 100644
index 000000000000..cebb6b90b7e0
--- /dev/null
+++ b/trunk/drivers/media/dvb/frontends/tda18271-common.c
@@ -0,0 +1,653 @@
+/*
+ tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "tda18271-priv.h"
+
+static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ enum tda18271_i2c_gate gate;
+ int ret = 0;
+
+ switch (priv->gate) {
+ case TDA18271_GATE_DIGITAL:
+ case TDA18271_GATE_ANALOG:
+ gate = priv->gate;
+ break;
+ case TDA18271_GATE_AUTO:
+ default:
+ switch (priv->mode) {
+ case TDA18271_DIGITAL:
+ gate = TDA18271_GATE_DIGITAL;
+ break;
+ case TDA18271_ANALOG:
+ default:
+ gate = TDA18271_GATE_ANALOG;
+ break;
+ }
+ }
+
+ switch (gate) {
+ case TDA18271_GATE_ANALOG:
+ if (fe->ops.analog_ops.i2c_gate_ctrl)
+ ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable);
+ break;
+ case TDA18271_GATE_DIGITAL:
+ if (fe->ops.i2c_gate_ctrl)
+ ret = fe->ops.i2c_gate_ctrl(fe, enable);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+};
+
+/*---------------------------------------------------------------------*/
+
+static void tda18271_dump_regs(struct dvb_frontend *fe, int extended)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda_reg("=== TDA18271 REG DUMP ===\n");
+ tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]);
+ tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]);
+ tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]);
+ tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]);
+ tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]);
+ tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]);
+ tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]);
+ tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]);
+ tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]);
+ tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]);
+ tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]);
+ tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]);
+ tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]);
+ tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]);
+ tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]);
+ tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]);
+
+ /* only dump extended regs if DBG_ADV is set */
+ if (!(tda18271_debug & DBG_ADV))
+ return;
+
+ /* W indicates write-only registers.
+ * Register dump for write-only registers shows last value written. */
+
+ tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]);
+ tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]);
+ tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]);
+ tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]);
+ tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]);
+ tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]);
+ tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]);
+ tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]);
+ tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]);
+ tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]);
+ tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]);
+ tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]);
+ tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]);
+ tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]);
+ tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]);
+ tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]);
+ tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]);
+ tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]);
+ tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]);
+ tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]);
+ tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]);
+ tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]);
+ tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]);
+}
+
+int tda18271_read_regs(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char buf = 0x00;
+ int ret;
+ struct i2c_msg msg[] = {
+ { .addr = priv->i2c_addr, .flags = 0,
+ .buf = &buf, .len = 1 },
+ { .addr = priv->i2c_addr, .flags = I2C_M_RD,
+ .buf = regs, .len = 16 }
+ };
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+
+ /* read all registers */
+ ret = i2c_transfer(priv->i2c_adap, msg, 2);
+
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ tda_err("ERROR: i2c_transfer returned: %d\n", ret);
+
+ if (tda18271_debug & DBG_REG)
+ tda18271_dump_regs(fe, 0);
+
+ return (ret == 2 ? 0 : ret);
+}
+
+int tda18271_read_extended(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char regdump[TDA18271_NUM_REGS];
+ unsigned char buf = 0x00;
+ int ret, i;
+ struct i2c_msg msg[] = {
+ { .addr = priv->i2c_addr, .flags = 0,
+ .buf = &buf, .len = 1 },
+ { .addr = priv->i2c_addr, .flags = I2C_M_RD,
+ .buf = regdump, .len = TDA18271_NUM_REGS }
+ };
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+
+ /* read all registers */
+ ret = i2c_transfer(priv->i2c_adap, msg, 2);
+
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ tda_err("ERROR: i2c_transfer returned: %d\n", ret);
+
+ for (i = 0; i <= TDA18271_NUM_REGS; i++) {
+ /* don't update write-only registers */
+ if ((i != R_EB9) &&
+ (i != R_EB16) &&
+ (i != R_EB17) &&
+ (i != R_EB19) &&
+ (i != R_EB20))
+ regs[i] = regdump[i];
+ }
+
+ if (tda18271_debug & DBG_REG)
+ tda18271_dump_regs(fe, 1);
+
+ return (ret == 2 ? 0 : ret);
+}
+
+int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char buf[TDA18271_NUM_REGS + 1];
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = len + 1 };
+ int i, ret;
+
+ BUG_ON((len == 0) || (idx + len > sizeof(buf)));
+
+ buf[0] = idx;
+ for (i = 1; i <= len; i++)
+ buf[i] = regs[idx - 1 + i];
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+
+ /* write registers */
+ ret = i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 1)
+ tda_err("ERROR: i2c_transfer returned: %d\n", ret);
+
+ return (ret == 1 ? 0 : ret);
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_init_regs(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda_dbg("initializing registers for device @ %d-%04x\n",
+ i2c_adapter_id(priv->i2c_adap), priv->i2c_addr);
+
+ /* initialize registers */
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_ID] = 0x83;
+ break;
+ case TDA18271HDC2:
+ regs[R_ID] = 0x84;
+ break;
+ };
+
+ regs[R_TM] = 0x08;
+ regs[R_PL] = 0x80;
+ regs[R_EP1] = 0xc6;
+ regs[R_EP2] = 0xdf;
+ regs[R_EP3] = 0x16;
+ regs[R_EP4] = 0x60;
+ regs[R_EP5] = 0x80;
+ regs[R_CPD] = 0x80;
+ regs[R_CD1] = 0x00;
+ regs[R_CD2] = 0x00;
+ regs[R_CD3] = 0x00;
+ regs[R_MPD] = 0x00;
+ regs[R_MD1] = 0x00;
+ regs[R_MD2] = 0x00;
+ regs[R_MD3] = 0x00;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB1] = 0xff;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB1] = 0xfc;
+ break;
+ };
+
+ regs[R_EB2] = 0x01;
+ regs[R_EB3] = 0x84;
+ regs[R_EB4] = 0x41;
+ regs[R_EB5] = 0x01;
+ regs[R_EB6] = 0x84;
+ regs[R_EB7] = 0x40;
+ regs[R_EB8] = 0x07;
+ regs[R_EB9] = 0x00;
+ regs[R_EB10] = 0x00;
+ regs[R_EB11] = 0x96;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB12] = 0x0f;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB12] = 0x33;
+ break;
+ };
+
+ regs[R_EB13] = 0xc1;
+ regs[R_EB14] = 0x00;
+ regs[R_EB15] = 0x8f;
+ regs[R_EB16] = 0x00;
+ regs[R_EB17] = 0x00;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB18] = 0x00;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB18] = 0x8c;
+ break;
+ };
+
+ regs[R_EB19] = 0x00;
+ regs[R_EB20] = 0x20;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB21] = 0x33;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB21] = 0xb3;
+ break;
+ };
+
+ regs[R_EB22] = 0x48;
+ regs[R_EB23] = 0xb0;
+
+ tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
+
+ /* setup agc1 gain */
+ regs[R_EB17] = 0x00;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x03;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x43;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x4c;
+ tda18271_write_regs(fe, R_EB17, 1);
+
+ /* setup agc2 gain */
+ if ((priv->id) == TDA18271HDC1) {
+ regs[R_EB20] = 0xa0;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xa7;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xe7;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xec;
+ tda18271_write_regs(fe, R_EB20, 1);
+ }
+
+ /* image rejection calibration */
+
+ /* low-band */
+ regs[R_EP3] = 0x1f;
+ regs[R_EP4] = 0x66;
+ regs[R_EP5] = 0x81;
+ regs[R_CPD] = 0xcc;
+ regs[R_CD1] = 0x6c;
+ regs[R_CD2] = 0x00;
+ regs[R_CD3] = 0x00;
+ regs[R_MPD] = 0xcd;
+ regs[R_MD1] = 0x77;
+ regs[R_MD2] = 0x08;
+ regs[R_MD3] = 0x00;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ tda18271_write_regs(fe, R_EP3, 11);
+ break;
+ case TDA18271HDC2:
+ tda18271_write_regs(fe, R_EP3, 12);
+ break;
+ };
+
+ if ((priv->id) == TDA18271HDC2) {
+ /* main pll cp source on */
+ regs[R_EB4] = 0x61;
+ tda18271_write_regs(fe, R_EB4, 1);
+ msleep(1);
+
+ /* main pll cp source off */
+ regs[R_EB4] = 0x41;
+ tda18271_write_regs(fe, R_EB4, 1);
+ }
+
+ msleep(5); /* pll locking */
+
+ /* launch detector */
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted low measurement */
+
+ regs[R_EP5] = 0x85;
+ regs[R_CPD] = 0xcb;
+ regs[R_CD1] = 0x66;
+ regs[R_CD2] = 0x70;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image low optimization completion */
+
+ /* mid-band */
+ regs[R_EP5] = 0x82;
+ regs[R_CPD] = 0xa8;
+ regs[R_CD2] = 0x00;
+ regs[R_MPD] = 0xa9;
+ regs[R_MD1] = 0x73;
+ regs[R_MD2] = 0x1a;
+
+ tda18271_write_regs(fe, R_EP3, 11);
+ msleep(5); /* pll locking */
+
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted mid measurement */
+
+ regs[R_EP5] = 0x86;
+ regs[R_CPD] = 0xa8;
+ regs[R_CD1] = 0x66;
+ regs[R_CD2] = 0xa0;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image mid optimization completion */
+
+ /* high-band */
+ regs[R_EP5] = 0x83;
+ regs[R_CPD] = 0x98;
+ regs[R_CD1] = 0x65;
+ regs[R_CD2] = 0x00;
+ regs[R_MPD] = 0x99;
+ regs[R_MD1] = 0x71;
+ regs[R_MD2] = 0xcd;
+
+ tda18271_write_regs(fe, R_EP3, 11);
+ msleep(5); /* pll locking */
+
+ /* launch detector */
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted high measurement */
+
+ regs[R_EP5] = 0x87;
+ regs[R_CD1] = 0x65;
+ regs[R_CD2] = 0x50;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image high optimization completion */
+
+ /* return to normal mode */
+ regs[R_EP4] = 0x64;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* synchronize */
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+/*
+ * Standby modes, EP3 [7:5]
+ *
+ * | SM || SM_LT || SM_XT || mode description
+ * |=====\\=======\\=======\\===================================
+ * | 0 || 0 || 0 || normal mode
+ * |-----||-------||-------||-----------------------------------
+ * | || || || standby mode w/ slave tuner output
+ * | 1 || 0 || 0 || & loop thru & xtal oscillator on
+ * |-----||-------||-------||-----------------------------------
+ * | 1 || 1 || 0 || standby mode w/ xtal oscillator on
+ * |-----||-------||-------||-----------------------------------
+ * | 1 || 1 || 1 || power off
+ *
+ */
+
+int tda18271_set_standby_mode(struct dvb_frontend *fe,
+ int sm, int sm_lt, int sm_xt)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt);
+
+ regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */
+ regs[R_EP3] |= sm ? (1 << 7) : 0 |
+ sm_lt ? (1 << 6) : 0 |
+ sm_xt ? (1 << 5) : 0;
+
+ tda18271_write_regs(fe, R_EP3, 1);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq)
+{
+ /* sets main post divider & divider bytes, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 d, pd;
+ u32 div;
+
+ int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_MPD] = (0x77 & pd);
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_MPD] &= ~0x08;
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_MPD] |= 0x08;
+ break;
+ }
+
+ div = ((d * (freq / 1000)) << 7) / 125;
+
+ regs[R_MD1] = 0x7f & (div >> 16);
+ regs[R_MD2] = 0xff & (div >> 8);
+ regs[R_MD3] = 0xff & div;
+fail:
+ return ret;
+}
+
+int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq)
+{
+ /* sets cal post divider & divider bytes, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 d, pd;
+ u32 div;
+
+ int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_CPD] = pd;
+
+ div = ((d * (freq / 1000)) << 7) / 125;
+
+ regs[R_CD1] = 0x7f & (div >> 16);
+ regs[R_CD2] = 0xff & (div >> 8);
+ regs[R_CD3] = 0xff & div;
+fail:
+ return ret;
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets bp filter bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EP1] &= ~0x07; /* clear bp filter bits */
+ regs[R_EP1] |= (0x07 & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets K & M bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EB13] &= ~0x7c; /* clear k & m bits */
+ regs[R_EB13] |= (0x7c & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets rf band bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EP2] &= ~0xe0; /* clear rf band bits */
+ regs[R_EP2] |= (0xe0 & (val << 5));
+fail:
+ return ret;
+}
+
+int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets gain taper bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EP2] &= ~0x1f; /* clear gain taper bits */
+ regs[R_EP2] |= (0x1f & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets IR Meas bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EP5] &= ~0x07;
+ regs[R_EP5] |= (0x07 & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets rf cal byte (RFC_Cprog), but does not write it */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ tda18271_lookup_map(fe, RF_CAL, freq, &val);
+
+ regs[R_EB14] = val;
+
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/trunk/drivers/media/dvb/frontends/tda18271-fe.c b/trunk/drivers/media/dvb/frontends/tda18271-fe.c
new file mode 100644
index 000000000000..dfe72aaec380
--- /dev/null
+++ b/trunk/drivers/media/dvb/frontends/tda18271-fe.c
@@ -0,0 +1,1225 @@
+/*
+ tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include
+#include
+#include "tda18271-priv.h"
+
+int tda18271_debug;
+module_param_named(debug, tda18271_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level "
+ "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))");
+
+static int tda18271_cal_on_startup;
+module_param_named(cal, tda18271_cal_on_startup, int, 0644);
+MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");
+
+static LIST_HEAD(tda18271_list);
+static DEFINE_MUTEX(tda18271_list_mutex);
+
+/*---------------------------------------------------------------------*/
+
+static int tda18271_ir_cal_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda18271_read_regs(fe);
+
+ /* test IR_CAL_OK to see if we need init */
+ if ((regs[R_EP1] & 0x08) == 0)
+ tda18271_init_regs(fe);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_channel_configuration(struct dvb_frontend *fe,
+ u32 ifc, u32 freq, u32 bw, u8 std,
+ int radio)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u32 N;
+
+ /* update TV broadcast parameters */
+
+ /* set standard */
+ regs[R_EP3] &= ~0x1f; /* clear std bits */
+ regs[R_EP3] |= std;
+
+ /* set cal mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* update IF output level & IF notch frequency */
+ regs[R_EP4] &= ~0x1c; /* clear if level bits */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_MPD] &= ~0x80; /* IF notch = 0 */
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_EP4] |= 0x04; /* IF level = 1 */
+ regs[R_MPD] |= 0x80; /* IF notch = 1 */
+ break;
+ }
+
+ if (radio)
+ regs[R_EP4] |= 0x80;
+ else
+ regs[R_EP4] &= ~0x80;
+
+ /* update RF_TOP / IF_TOP */
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_EB22] = 0x2c;
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_EB22] = 0x37;
+ break;
+ }
+ tda18271_write_regs(fe, R_EB22, 1);
+
+ /* --------------------------------------------------------------- */
+
+ /* disable Power Level Indicator */
+ regs[R_EP1] |= 0x40;
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_ir_measure(fe, &freq);
+
+ tda18271_calc_bp_filter(fe, &freq);
+
+ tda18271_calc_rf_band(fe, &freq);
+
+ tda18271_calc_gain_taper(fe, &freq);
+
+ /* --------------------------------------------------------------- */
+
+ /* dual tuner and agc1 extra configuration */
+
+ /* main vco when Master, cal vco when slave */
+ regs[R_EB1] |= 0x04; /* FIXME: assumes master */
+
+ /* agc1 always active */
+ regs[R_EB1] &= ~0x02;
+
+ /* agc1 has priority on agc2 */
+ regs[R_EB1] &= ~0x01;
+
+ tda18271_write_regs(fe, R_EB1, 1);
+
+ /* --------------------------------------------------------------- */
+
+ N = freq + ifc;
+
+ /* FIXME: assumes master */
+ tda18271_calc_main_pll(fe, N);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ tda18271_write_regs(fe, R_TM, 7);
+
+ /* main pll charge pump source */
+ regs[R_EB4] |= 0x20;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ msleep(1);
+
+ /* normal operation for the main pll */
+ regs[R_EB4] &= ~0x20;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ msleep(5);
+
+ return 0;
+}
+
+static int tda18271_read_thermometer(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int tm;
+
+ /* switch thermometer on */
+ regs[R_TM] |= 0x10;
+ tda18271_write_regs(fe, R_TM, 1);
+
+ /* read thermometer info */
+ tda18271_read_regs(fe);
+
+ if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) ||
+ (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) {
+
+ if ((regs[R_TM] & 0x20) == 0x20)
+ regs[R_TM] &= ~0x20;
+ else
+ regs[R_TM] |= 0x20;
+
+ tda18271_write_regs(fe, R_TM, 1);
+
+ msleep(10); /* temperature sensing */
+
+ /* read thermometer info */
+ tda18271_read_regs(fe);
+ }
+
+ tm = tda18271_lookup_thermometer(fe);
+
+ /* switch thermometer off */
+ regs[R_TM] &= ~0x10;
+ tda18271_write_regs(fe, R_TM, 1);
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ return tm;
+}
+
+static int tda18271_rf_tracking_filters_correction(struct dvb_frontend *fe,
+ u32 freq)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
+ unsigned char *regs = priv->tda18271_regs;
+ int tm_current, rfcal_comp, approx, i;
+ u8 dc_over_dt, rf_tab;
+
+ /* power up */
+ tda18271_set_standby_mode(fe, 0, 0, 0);
+
+ /* read die current temperature */
+ tm_current = tda18271_read_thermometer(fe);
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_rf_cal(fe, &freq);
+ rf_tab = regs[R_EB14];
+
+ i = tda18271_lookup_rf_band(fe, &freq, NULL);
+ if (i < 0)
+ return -EINVAL;
+
+ if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) {
+ approx = map[i].rf_a1 *
+ (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab;
+ } else {
+ approx = map[i].rf_a2 *
+ (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab;
+ }
+
+ if (approx < 0)
+ approx = 0;
+ if (approx > 255)
+ approx = 255;
+
+ tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);
+
+ /* calculate temperature compensation */
+ rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal);
+
+ regs[R_EB14] = approx + rfcal_comp;
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ return 0;
+}
+
+static int tda18271_por(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ /* power up detector 1 */
+ regs[R_EB12] &= ~0x20;
+ tda18271_write_regs(fe, R_EB12, 1);
+
+ regs[R_EB18] &= ~0x80; /* turn agc1 loop on */
+ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */
+
+ /* POR mode */
+ tda18271_set_standby_mode(fe, 1, 0, 0);
+
+ /* disable 1.5 MHz low pass filter */
+ regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */
+ regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */
+ tda18271_write_regs(fe, R_EB21, 3);
+
+ return 0;
+}
+
+static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u32 N;
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* switch off agc1 */
+ regs[R_EP3] |= 0x40; /* sm_lt = 1 */
+
+ regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_bp_filter(fe, &freq);
+ tda18271_calc_gain_taper(fe, &freq);
+ tda18271_calc_rf_band(fe, &freq);
+ tda18271_calc_km(fe, &freq);
+
+ tda18271_write_regs(fe, R_EP1, 3);
+ tda18271_write_regs(fe, R_EB13, 1);
+
+ /* main pll charge pump source */
+ regs[R_EB4] |= 0x20;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ /* cal pll charge pump source */
+ regs[R_EB7] |= 0x20;
+ tda18271_write_regs(fe, R_EB7, 1);
+
+ /* force dcdc converter to 0 V */
+ regs[R_EB14] = 0x00;
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ /* disable plls lock */
+ regs[R_EB20] &= ~0x20;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ /* set CAL mode to RF tracking filter calibration */
+ regs[R_EP4] |= 0x03;
+ tda18271_write_regs(fe, R_EP4, 2);
+
+ /* --------------------------------------------------------------- */
+
+ /* set the internal calibration signal */
+ N = freq;
+
+ tda18271_calc_main_pll(fe, N);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ /* downconvert internal calibration */
+ N += 1000000;
+
+ tda18271_calc_main_pll(fe, N);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ msleep(5);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* --------------------------------------------------------------- */
+
+ /* normal operation for the main pll */
+ regs[R_EB4] &= ~0x20;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ /* normal operation for the cal pll */
+ regs[R_EB7] &= ~0x20;
+ tda18271_write_regs(fe, R_EB7, 1);
+
+ msleep(5); /* plls locking */
+
+ /* launch the rf tracking filters calibration */
+ regs[R_EB20] |= 0x20;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ msleep(60); /* calibration */
+
+ /* --------------------------------------------------------------- */
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* switch on agc1 */
+ regs[R_EP3] &= ~0x40; /* sm_lt = 0 */
+
+ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ tda18271_write_regs(fe, R_EP3, 2);
+
+ /* synchronization */
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* get calibration result */
+ tda18271_read_extended(fe);
+
+ return regs[R_EB14];
+}
+
+static int tda18271_powerscan(struct dvb_frontend *fe,
+ u32 *freq_in, u32 *freq_out)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int sgn, bcal, count, wait;
+ u8 cid_target;
+ u16 count_limit;
+ u32 freq;
+
+ freq = *freq_in;
+
+ tda18271_calc_rf_band(fe, &freq);
+ tda18271_calc_rf_cal(fe, &freq);
+ tda18271_calc_gain_taper(fe, &freq);
+ tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ /* downconvert frequency */
+ freq += 1000000;
+
+ tda18271_calc_main_pll(fe, freq);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ msleep(5); /* pll locking */
+
+ /* detection mode */
+ regs[R_EP4] &= ~0x03;
+ regs[R_EP4] |= 0x01;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* launch power detection measurement */
+ tda18271_write_regs(fe, R_EP2, 1);
+
+ /* read power detection info, stored in EB10 */
+ tda18271_read_extended(fe);
+
+ /* algorithm initialization */
+ sgn = 1;
+ *freq_out = *freq_in;
+ bcal = 0;
+ count = 0;
+ wait = false;
+
+ while ((regs[R_EB10] & 0x3f) < cid_target) {
+ /* downconvert updated freq to 1 MHz */
+ freq = *freq_in + (sgn * count) + 1000000;
+
+ tda18271_calc_main_pll(fe, freq);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ if (wait) {
+ msleep(5); /* pll locking */
+ wait = false;
+ } else
+ udelay(100); /* pll locking */
+
+ /* launch power detection measurement */
+ tda18271_write_regs(fe, R_EP2, 1);
+
+ /* read power detection info, stored in EB10 */
+ tda18271_read_extended(fe);
+
+ count += 200;
+
+ if (count < count_limit)
+ continue;
+
+ if (sgn <= 0)
+ break;
+
+ sgn = -1 * sgn;
+ count = 200;
+ wait = true;
+ }
+
+ if ((regs[R_EB10] & 0x3f) >= cid_target) {
+ bcal = 1;
+ *freq_out = freq - 1000000;
+ } else
+ bcal = 0;
+
+ tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n",
+ bcal, *freq_in, *freq_out, freq);
+
+ return bcal;
+}
+
+static int tda18271_powerscan_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ /* set standard to digital */
+ regs[R_EP3] &= ~0x1f; /* clear std bits */
+ regs[R_EP3] |= 0x12;
+
+ /* set cal mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* update IF output level & IF notch frequency */
+ regs[R_EP4] &= ~0x1c; /* clear if level bits */
+
+ tda18271_write_regs(fe, R_EP3, 2);
+
+ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */
+
+ /* 1.5 MHz low pass filter */
+ regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */
+ regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */
+
+ tda18271_write_regs(fe, R_EB21, 3);
+
+ return 0;
+}
+
+static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
+ unsigned char *regs = priv->tda18271_regs;
+ int bcal, rf, i;
+#define RF1 0
+#define RF2 1
+#define RF3 2
+ u32 rf_default[3];
+ u32 rf_freq[3];
+ u8 prog_cal[3];
+ u8 prog_tab[3];
+
+ i = tda18271_lookup_rf_band(fe, &freq, NULL);
+
+ if (i < 0)
+ return i;
+
+ rf_default[RF1] = 1000 * map[i].rf1_def;
+ rf_default[RF2] = 1000 * map[i].rf2_def;
+ rf_default[RF3] = 1000 * map[i].rf3_def;
+
+ for (rf = RF1; rf <= RF3; rf++) {
+ if (0 == rf_default[rf])
+ return 0;
+ tda_cal("freq = %d, rf = %d\n", freq, rf);
+
+ /* look for optimized calibration frequency */
+ bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]);
+
+ tda18271_calc_rf_cal(fe, &rf_freq[rf]);
+ prog_tab[rf] = regs[R_EB14];
+
+ if (1 == bcal)
+ prog_cal[rf] = tda18271_calibrate_rf(fe, rf_freq[rf]);
+ else
+ prog_cal[rf] = prog_tab[rf];
+
+ switch (rf) {
+ case RF1:
+ map[i].rf_a1 = 0;
+ map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1];
+ map[i].rf1 = rf_freq[RF1] / 1000;
+ break;
+ case RF2:
+ map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] -
+ prog_cal[RF1] + prog_tab[RF1]) /
+ ((rf_freq[RF2] - rf_freq[RF1]) / 1000);
+ map[i].rf2 = rf_freq[RF2] / 1000;
+ break;
+ case RF3:
+ map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] -
+ prog_cal[RF2] + prog_tab[RF2]) /
+ ((rf_freq[RF3] - rf_freq[RF2]) / 1000);
+ map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2];
+ map[i].rf3 = rf_freq[RF3] / 1000;
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ return 0;
+}
+
+static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned int i;
+
+ tda_info("tda18271: performing RF tracking filter calibration\n");
+
+ /* wait for die temperature stabilization */
+ msleep(200);
+
+ tda18271_powerscan_init(fe);
+
+ /* rf band calibration */
+ for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++)
+ tda18271_rf_tracking_filters_init(fe, 1000 *
+ priv->rf_cal_state[i].rfmax);
+
+ priv->tm_rfcal = tda18271_read_thermometer(fe);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_rf_cal_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ /* test RF_CAL_OK to see if we need init */
+ if ((regs[R_EP1] & 0x10) == 0)
+ priv->cal_initialized = false;
+
+ if (priv->cal_initialized)
+ return 0;
+
+ tda18271_calc_rf_filter_curve(fe);
+
+ tda18271_por(fe);
+
+ tda_info("tda18271: RF tracking filter calibration complete\n");
+
+ priv->cal_initialized = true;
+
+ return 0;
+}
+
+static int tda18271_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ mutex_lock(&priv->lock);
+
+ /* power up */
+ tda18271_set_standby_mode(fe, 0, 0, 0);
+
+ /* initialization */
+ tda18271_ir_cal_init(fe);
+
+ if (priv->id == TDA18271HDC2)
+ tda18271_rf_cal_init(fe);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int tda18271c2_tune(struct dvb_frontend *fe,
+ u32 ifc, u32 freq, u32 bw, u8 std, int radio)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ tda_dbg("freq = %d, ifc = %d\n", freq, ifc);
+
+ tda18271_init(fe);
+
+ mutex_lock(&priv->lock);
+
+ tda18271_rf_tracking_filters_correction(fe, freq);
+
+ tda18271_channel_configuration(fe, ifc, freq, bw, std, radio);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271c1_tune(struct dvb_frontend *fe,
+ u32 ifc, u32 freq, u32 bw, u8 std, int radio)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u32 N = 0;
+
+ tda18271_init(fe);
+
+ mutex_lock(&priv->lock);
+
+ tda_dbg("freq = %d, ifc = %d\n", freq, ifc);
+
+ /* RF tracking filter calibration */
+
+ /* calculate bp filter */
+ tda18271_calc_bp_filter(fe, &freq);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ regs[R_EB4] &= 0x07;
+ regs[R_EB4] |= 0x60;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ regs[R_EB7] = 0x60;
+ tda18271_write_regs(fe, R_EB7, 1);
+
+ regs[R_EB14] = 0x00;
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ regs[R_EB20] = 0xcc;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ /* set cal mode to RF tracking filter calibration */
+ regs[R_EP4] |= 0x03;
+
+ /* calculate cal pll */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ N = freq - 1250000;
+ break;
+ case TDA18271_DIGITAL:
+ N = freq + bw / 2;
+ break;
+ }
+
+ tda18271_calc_cal_pll(fe, N);
+
+ /* calculate main pll */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ N = freq - 250000;
+ break;
+ case TDA18271_DIGITAL:
+ N = freq + bw / 2 + 1000000;
+ break;
+ }
+
+ tda18271_calc_main_pll(fe, N);
+
+ tda18271_write_regs(fe, R_EP3, 11);
+ msleep(5); /* RF tracking filter calibration initialization */
+
+ /* search for K,M,CO for RF calibration */
+ tda18271_calc_km(fe, &freq);
+ tda18271_write_regs(fe, R_EB13, 1);
+
+ /* search for rf band */
+ tda18271_calc_rf_band(fe, &freq);
+
+ /* search for gain taper */
+ tda18271_calc_gain_taper(fe, &freq);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ regs[R_EB4] &= 0x07;
+ regs[R_EB4] |= 0x40;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ regs[R_EB7] = 0x40;
+ tda18271_write_regs(fe, R_EB7, 1);
+ msleep(10);
+
+ regs[R_EB20] = 0xec;
+ tda18271_write_regs(fe, R_EB20, 1);
+ msleep(60); /* RF tracking filter calibration completion */
+
+ regs[R_EP4] &= ~0x03; /* set cal mode to normal */
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* RF tracking filter correction for VHF_Low band */
+ if (0 == tda18271_calc_rf_cal(fe, &freq))
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ /* Channel Configuration */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_EB22] = 0x2c;
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_EB22] = 0x37;
+ break;
+ }
+ tda18271_write_regs(fe, R_EB22, 1);
+
+ regs[R_EP1] |= 0x40; /* set dis power level on */
+
+ /* set standard */
+ regs[R_EP3] &= ~0x1f; /* clear std bits */
+
+ /* see table 22 */
+ regs[R_EP3] |= std;
+
+ regs[R_EP4] &= ~0x03; /* set cal mode to normal */
+
+ regs[R_EP4] &= ~0x1c; /* clear if level bits */
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_MPD] &= ~0x80; /* IF notch = 0 */
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_EP4] |= 0x04;
+ regs[R_MPD] |= 0x80;
+ break;
+ }
+
+ if (radio)
+ regs[R_EP4] |= 0x80;
+ else
+ regs[R_EP4] &= ~0x80;
+
+ /* image rejection validity */
+ tda18271_calc_ir_measure(fe, &freq);
+
+ /* calculate MAIN PLL */
+ N = freq + ifc;
+
+ tda18271_calc_main_pll(fe, N);
+
+ tda18271_write_regs(fe, R_TM, 15);
+ msleep(5);
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static inline int tda18271_tune(struct dvb_frontend *fe,
+ u32 ifc, u32 freq, u32 bw, u8 std, int radio)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ int ret = -EINVAL;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ ret = tda18271c1_tune(fe, ifc, freq, bw, std, radio);
+ break;
+ case TDA18271HDC2:
+ ret = tda18271c2_tune(fe, ifc, freq, bw, std, radio);
+ break;
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std_map = &priv->std;
+ int ret;
+ u8 std;
+ u16 sgIF;
+ u32 bw, freq = params->frequency;
+
+ priv->mode = TDA18271_DIGITAL;
+
+ if (fe->ops.info.type == FE_ATSC) {
+ switch (params->u.vsb.modulation) {
+ case VSB_8:
+ case VSB_16:
+ std = std_map->atsc_6.std_bits;
+ sgIF = std_map->atsc_6.if_freq;
+ break;
+ case QAM_64:
+ case QAM_256:
+ std = std_map->qam_6.std_bits;
+ sgIF = std_map->qam_6.if_freq;
+ break;
+ default:
+ tda_warn("modulation not set!\n");
+ return -EINVAL;
+ }
+#if 0
+ /* userspace request is already center adjusted */
+ freq += 1750000; /* Adjust to center (+1.75MHZ) */
+#endif
+ bw = 6000000;
+ } else if (fe->ops.info.type == FE_OFDM) {
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ bw = 6000000;
+ std = std_map->dvbt_6.std_bits;
+ sgIF = std_map->dvbt_6.if_freq;
+ break;
+ case BANDWIDTH_7_MHZ:
+ bw = 7000000;
+ std = std_map->dvbt_7.std_bits;
+ sgIF = std_map->dvbt_7.if_freq;
+ break;
+ case BANDWIDTH_8_MHZ:
+ bw = 8000000;
+ std = std_map->dvbt_8.std_bits;
+ sgIF = std_map->dvbt_8.if_freq;
+ break;
+ default:
+ tda_warn("bandwidth not set!\n");
+ return -EINVAL;
+ }
+ } else {
+ tda_warn("modulation type not supported!\n");
+ return -EINVAL;
+ }
+
+ /* When tuning digital, the analog demod must be tri-stated */
+ if (fe->ops.analog_ops.standby)
+ fe->ops.analog_ops.standby(fe);
+
+ ret = tda18271_tune(fe, sgIF * 1000, freq, bw, std, 0);
+
+ if (ret < 0)
+ goto fail;
+
+ priv->frequency = freq;
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ?
+ params->u.ofdm.bandwidth : 0;
+fail:
+ return ret;
+}
+
+static int tda18271_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std_map = &priv->std;
+ char *mode;
+ int ret, radio = 0;
+ u8 std;
+ u16 sgIF;
+ u32 freq = params->frequency * 62500;
+
+ priv->mode = TDA18271_ANALOG;
+
+ if (params->mode == V4L2_TUNER_RADIO) {
+ radio = 1;
+ freq = freq / 1000;
+ std = std_map->fm_radio.std_bits;
+ sgIF = std_map->fm_radio.if_freq;
+ mode = "fm";
+ } else if (params->std & V4L2_STD_MN) {
+ std = std_map->atv_mn.std_bits;
+ sgIF = std_map->atv_mn.if_freq;
+ mode = "MN";
+ } else if (params->std & V4L2_STD_B) {
+ std = std_map->atv_b.std_bits;
+ sgIF = std_map->atv_b.if_freq;
+ mode = "B";
+ } else if (params->std & V4L2_STD_GH) {
+ std = std_map->atv_gh.std_bits;
+ sgIF = std_map->atv_gh.if_freq;
+ mode = "GH";
+ } else if (params->std & V4L2_STD_PAL_I) {
+ std = std_map->atv_i.std_bits;
+ sgIF = std_map->atv_i.if_freq;
+ mode = "I";
+ } else if (params->std & V4L2_STD_DK) {
+ std = std_map->atv_dk.std_bits;
+ sgIF = std_map->atv_dk.if_freq;
+ mode = "DK";
+ } else if (params->std & V4L2_STD_SECAM_L) {
+ std = std_map->atv_l.std_bits;
+ sgIF = std_map->atv_l.if_freq;
+ mode = "L";
+ } else if (params->std & V4L2_STD_SECAM_LC) {
+ std = std_map->atv_lc.std_bits;
+ sgIF = std_map->atv_lc.if_freq;
+ mode = "L'";
+ } else {
+ std = std_map->atv_i.std_bits;
+ sgIF = std_map->atv_i.if_freq;
+ mode = "xx";
+ }
+
+ tda_dbg("setting tda18271 to system %s\n", mode);
+
+ ret = tda18271_tune(fe, sgIF * 1000, freq, 0, std, radio);
+
+ if (ret < 0)
+ goto fail;
+
+ priv->frequency = freq;
+ priv->bandwidth = 0;
+fail:
+ return ret;
+}
+
+static int tda18271_sleep(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ mutex_lock(&priv->lock);
+
+ /* standby mode w/ slave tuner output
+ * & loop thru & xtal oscillator on */
+ tda18271_set_standby_mode(fe, 1, 0, 0);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int tda18271_release(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ mutex_lock(&tda18271_list_mutex);
+
+ priv->count--;
+
+ if (!priv->count) {
+ tda_dbg("destroying instance @ %d-%04x\n",
+ i2c_adapter_id(priv->i2c_adap),
+ priv->i2c_addr);
+ list_del(&priv->tda18271_list);
+
+ kfree(priv);
+ }
+ mutex_unlock(&tda18271_list_mutex);
+
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+#define tda18271_update_std(std_cfg, name) do { \
+ if (map->std_cfg.if_freq + map->std_cfg.std_bits > 0) { \
+ tda_dbg("Using custom std config for %s\n", name); \
+ memcpy(&std->std_cfg, &map->std_cfg, \
+ sizeof(struct tda18271_std_map_item)); \
+ } } while (0)
+
+#define tda18271_dump_std_item(std_cfg, name) do { \
+ tda_dbg("(%s) if freq = %d, std bits = 0x%02x\n", \
+ name, std->std_cfg.if_freq, std->std_cfg.std_bits); \
+ } while (0)
+
+static int tda18271_dump_std_map(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std = &priv->std;
+
+ tda_dbg("========== STANDARD MAP SETTINGS ==========\n");
+ tda18271_dump_std_item(fm_radio, "fm");
+ tda18271_dump_std_item(atv_b, "pal b");
+ tda18271_dump_std_item(atv_dk, "pal dk");
+ tda18271_dump_std_item(atv_gh, "pal gh");
+ tda18271_dump_std_item(atv_i, "pal i");
+ tda18271_dump_std_item(atv_l, "pal l");
+ tda18271_dump_std_item(atv_lc, "pal l'");
+ tda18271_dump_std_item(atv_mn, "atv mn");
+ tda18271_dump_std_item(atsc_6, "atsc 6");
+ tda18271_dump_std_item(dvbt_6, "dvbt 6");
+ tda18271_dump_std_item(dvbt_7, "dvbt 7");
+ tda18271_dump_std_item(dvbt_8, "dvbt 8");
+ tda18271_dump_std_item(qam_6, "qam 6");
+ tda18271_dump_std_item(qam_8, "qam 8");
+
+ return 0;
+}
+
+static int tda18271_update_std_map(struct dvb_frontend *fe,
+ struct tda18271_std_map *map)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std = &priv->std;
+
+ if (!map)
+ return -EINVAL;
+
+ tda18271_update_std(fm_radio, "fm");
+ tda18271_update_std(atv_b, "atv b");
+ tda18271_update_std(atv_dk, "atv dk");
+ tda18271_update_std(atv_gh, "atv gh");
+ tda18271_update_std(atv_i, "atv i");
+ tda18271_update_std(atv_l, "atv l");
+ tda18271_update_std(atv_lc, "atv l'");
+ tda18271_update_std(atv_mn, "atv mn");
+ tda18271_update_std(atsc_6, "atsc 6");
+ tda18271_update_std(dvbt_6, "dvbt 6");
+ tda18271_update_std(dvbt_7, "dvbt 7");
+ tda18271_update_std(dvbt_8, "dvbt 8");
+ tda18271_update_std(qam_6, "qam 6");
+ tda18271_update_std(qam_8, "qam 8");
+
+ return 0;
+}
+
+static int tda18271_get_id(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ char *name;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+ tda18271_read_regs(fe);
+ mutex_unlock(&priv->lock);
+
+ switch (regs[R_ID] & 0x7f) {
+ case 3:
+ name = "TDA18271HD/C1";
+ priv->id = TDA18271HDC1;
+ break;
+ case 4:
+ name = "TDA18271HD/C2";
+ priv->id = TDA18271HDC2;
+ break;
+ default:
+ name = "Unknown device";
+ ret = -EINVAL;
+ break;
+ }
+
+ tda_info("%s detected @ %d-%04x%s\n", name,
+ i2c_adapter_id(priv->i2c_adap), priv->i2c_addr,
+ (0 == ret) ? "" : ", device not supported.");
+
+ return ret;
+}
+
+static struct dvb_tuner_ops tda18271_tuner_ops = {
+ .info = {
+ .name = "NXP TDA18271HD",
+ .frequency_min = 45000000,
+ .frequency_max = 864000000,
+ .frequency_step = 62500
+ },
+ .init = tda18271_init,
+ .sleep = tda18271_sleep,
+ .set_params = tda18271_set_params,
+ .set_analog_params = tda18271_set_analog_params,
+ .release = tda18271_release,
+ .get_frequency = tda18271_get_frequency,
+ .get_bandwidth = tda18271_get_bandwidth,
+};
+
+struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg)
+{
+ struct tda18271_priv *priv = NULL;
+ int state_found = 0;
+
+ mutex_lock(&tda18271_list_mutex);
+
+ list_for_each_entry(priv, &tda18271_list, tda18271_list) {
+ if ((i2c_adapter_id(priv->i2c_adap) == i2c_adapter_id(i2c)) &&
+ (priv->i2c_addr == addr)) {
+ tda_dbg("attaching existing tuner @ %d-%04x\n",
+ i2c_adapter_id(priv->i2c_adap),
+ priv->i2c_addr);
+ priv->count++;
+ fe->tuner_priv = priv;
+ state_found = 1;
+ /* allow dvb driver to override i2c gate setting */
+ if ((cfg) && (cfg->gate != TDA18271_GATE_ANALOG))
+ priv->gate = cfg->gate;
+ break;
+ }
+ }
+ if (state_found == 0) {
+ tda_dbg("creating new tuner instance @ %d-%04x\n",
+ i2c_adapter_id(i2c), addr);
+
+ priv = kzalloc(sizeof(struct tda18271_priv), GFP_KERNEL);
+ if (priv == NULL) {
+ mutex_unlock(&tda18271_list_mutex);
+ return NULL;
+ }
+
+ priv->i2c_addr = addr;
+ priv->i2c_adap = i2c;
+ priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
+ priv->cal_initialized = false;
+ mutex_init(&priv->lock);
+ priv->count++;
+
+ fe->tuner_priv = priv;
+
+ list_add_tail(&priv->tda18271_list, &tda18271_list);
+
+ if (tda18271_get_id(fe) < 0)
+ goto fail;
+
+ if (tda18271_assign_map_layout(fe) < 0)
+ goto fail;
+
+ mutex_lock(&priv->lock);
+ tda18271_init_regs(fe);
+
+ if ((tda18271_cal_on_startup) && (priv->id == TDA18271HDC2))
+ tda18271_rf_cal_init(fe);
+
+ mutex_unlock(&priv->lock);
+ }
+
+ /* override default std map with values in config struct */
+ if ((cfg) && (cfg->std_map))
+ tda18271_update_std_map(fe, cfg->std_map);
+
+ mutex_unlock(&tda18271_list_mutex);
+
+ memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ if (tda18271_debug & DBG_MAP)
+ tda18271_dump_std_map(fe);
+
+ return fe;
+fail:
+ mutex_unlock(&tda18271_list_mutex);
+
+ tda18271_release(fe);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(tda18271_attach);
+MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");
+MODULE_AUTHOR("Michael Krufky ");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/trunk/drivers/media/dvb/frontends/tda18271-priv.h b/trunk/drivers/media/dvb/frontends/tda18271-priv.h
new file mode 100644
index 000000000000..7b939a5325fb
--- /dev/null
+++ b/trunk/drivers/media/dvb/frontends/tda18271-priv.h
@@ -0,0 +1,212 @@
+/*
+ tda18271-priv.h - private header for the NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __TDA18271_PRIV_H__
+#define __TDA18271_PRIV_H__
+
+#include
+#include
+#include
+#include "tda18271.h"
+
+#define R_ID 0x00 /* ID byte */
+#define R_TM 0x01 /* Thermo byte */
+#define R_PL 0x02 /* Power level byte */
+#define R_EP1 0x03 /* Easy Prog byte 1 */
+#define R_EP2 0x04 /* Easy Prog byte 2 */
+#define R_EP3 0x05 /* Easy Prog byte 3 */
+#define R_EP4 0x06 /* Easy Prog byte 4 */
+#define R_EP5 0x07 /* Easy Prog byte 5 */
+#define R_CPD 0x08 /* Cal Post-Divider byte */
+#define R_CD1 0x09 /* Cal Divider byte 1 */
+#define R_CD2 0x0a /* Cal Divider byte 2 */
+#define R_CD3 0x0b /* Cal Divider byte 3 */
+#define R_MPD 0x0c /* Main Post-Divider byte */
+#define R_MD1 0x0d /* Main Divider byte 1 */
+#define R_MD2 0x0e /* Main Divider byte 2 */
+#define R_MD3 0x0f /* Main Divider byte 3 */
+#define R_EB1 0x10 /* Extended byte 1 */
+#define R_EB2 0x11 /* Extended byte 2 */
+#define R_EB3 0x12 /* Extended byte 3 */
+#define R_EB4 0x13 /* Extended byte 4 */
+#define R_EB5 0x14 /* Extended byte 5 */
+#define R_EB6 0x15 /* Extended byte 6 */
+#define R_EB7 0x16 /* Extended byte 7 */
+#define R_EB8 0x17 /* Extended byte 8 */
+#define R_EB9 0x18 /* Extended byte 9 */
+#define R_EB10 0x19 /* Extended byte 10 */
+#define R_EB11 0x1a /* Extended byte 11 */
+#define R_EB12 0x1b /* Extended byte 12 */
+#define R_EB13 0x1c /* Extended byte 13 */
+#define R_EB14 0x1d /* Extended byte 14 */
+#define R_EB15 0x1e /* Extended byte 15 */
+#define R_EB16 0x1f /* Extended byte 16 */
+#define R_EB17 0x20 /* Extended byte 17 */
+#define R_EB18 0x21 /* Extended byte 18 */
+#define R_EB19 0x22 /* Extended byte 19 */
+#define R_EB20 0x23 /* Extended byte 20 */
+#define R_EB21 0x24 /* Extended byte 21 */
+#define R_EB22 0x25 /* Extended byte 22 */
+#define R_EB23 0x26 /* Extended byte 23 */
+
+#define TDA18271_NUM_REGS 39
+
+/*---------------------------------------------------------------------*/
+
+struct tda18271_rf_tracking_filter_cal {
+ u32 rfmax;
+ u8 rfband;
+ u32 rf1_def;
+ u32 rf2_def;
+ u32 rf3_def;
+ u32 rf1;
+ u32 rf2;
+ u32 rf3;
+ int rf_a1;
+ int rf_b1;
+ int rf_a2;
+ int rf_b2;
+};
+
+enum tda18271_mode {
+ TDA18271_ANALOG,
+ TDA18271_DIGITAL,
+};
+
+struct tda18271_map_layout;
+
+enum tda18271_ver {
+ TDA18271HDC1,
+ TDA18271HDC2,
+};
+
+struct tda18271_priv {
+ u8 i2c_addr;
+ struct i2c_adapter *i2c_adap;
+ unsigned char tda18271_regs[TDA18271_NUM_REGS];
+
+ struct list_head tda18271_list;
+
+ enum tda18271_mode mode;
+ enum tda18271_i2c_gate gate;
+ enum tda18271_ver id;
+
+ unsigned int count;
+ unsigned int tm_rfcal;
+ unsigned int cal_initialized:1;
+
+ struct tda18271_map_layout *maps;
+ struct tda18271_std_map std;
+ struct tda18271_rf_tracking_filter_cal rf_cal_state[8];
+
+ struct mutex lock;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+/*---------------------------------------------------------------------*/
+
+extern int tda18271_debug;
+
+#define DBG_INFO 1
+#define DBG_MAP 2
+#define DBG_REG 4
+#define DBG_ADV 8
+#define DBG_CAL 16
+
+#define tda_printk(kern, fmt, arg...) \
+ printk(kern "%s: " fmt, __FUNCTION__, ##arg)
+
+#define dprintk(kern, lvl, fmt, arg...) do {\
+ if (tda18271_debug & lvl) \
+ tda_printk(kern, fmt, ##arg); } while (0)
+
+#define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
+#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg)
+#define tda_err(fmt, arg...) tda_printk(KERN_ERR, fmt, ##arg)
+#define tda_dbg(fmt, arg...) dprintk(KERN_DEBUG, DBG_INFO, fmt, ##arg)
+#define tda_map(fmt, arg...) dprintk(KERN_DEBUG, DBG_MAP, fmt, ##arg)
+#define tda_reg(fmt, arg...) dprintk(KERN_DEBUG, DBG_REG, fmt, ##arg)
+#define tda_cal(fmt, arg...) dprintk(KERN_DEBUG, DBG_CAL, fmt, ##arg)
+
+/*---------------------------------------------------------------------*/
+
+enum tda18271_map_type {
+ /* tda18271_pll_map */
+ MAIN_PLL,
+ CAL_PLL,
+ /* tda18271_map */
+ RF_CAL,
+ RF_CAL_KMCO,
+ RF_CAL_DC_OVER_DT,
+ BP_FILTER,
+ RF_BAND,
+ GAIN_TAPER,
+ IR_MEASURE,
+};
+
+extern int tda18271_lookup_pll_map(struct dvb_frontend *fe,
+ enum tda18271_map_type map_type,
+ u32 *freq, u8 *post_div, u8 *div);
+extern int tda18271_lookup_map(struct dvb_frontend *fe,
+ enum tda18271_map_type map_type,
+ u32 *freq, u8 *val);
+
+extern int tda18271_lookup_thermometer(struct dvb_frontend *fe);
+
+extern int tda18271_lookup_rf_band(struct dvb_frontend *fe,
+ u32 *freq, u8 *rf_band);
+
+extern int tda18271_lookup_cid_target(struct dvb_frontend *fe,
+ u32 *freq, u8 *cid_target,
+ u16 *count_limit);
+
+extern int tda18271_assign_map_layout(struct dvb_frontend *fe);
+
+/*---------------------------------------------------------------------*/
+
+extern int tda18271_read_regs(struct dvb_frontend *fe);
+extern int tda18271_read_extended(struct dvb_frontend *fe);
+extern int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len);
+extern int tda18271_init_regs(struct dvb_frontend *fe);
+
+extern int tda18271_set_standby_mode(struct dvb_frontend *fe,
+ int sm, int sm_lt, int sm_xt);
+
+extern int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq);
+extern int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq);
+
+extern int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq);
+
+#endif /* __TDA18271_PRIV_H__ */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/trunk/drivers/media/dvb/frontends/tda18271-tables.c b/trunk/drivers/media/dvb/frontends/tda18271-tables.c
new file mode 100644
index 000000000000..e94afcfdc5bc
--- /dev/null
+++ b/trunk/drivers/media/dvb/frontends/tda18271-tables.c
@@ -0,0 +1,1285 @@
+/*
+ tda18271-tables.c - driver for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "tda18271-priv.h"
+
+struct tda18271_pll_map {
+ u32 lomax;
+ u8 pd; /* post div */
+ u8 d; /* div */
+};
+
+struct tda18271_map {
+ u32 rfmax;
+ u8 val;
+};
+
+/*---------------------------------------------------------------------*/
+
+static struct tda18271_pll_map tda18271c1_main_pll[] = {
+ { .lomax = 32000, .pd = 0x5f, .d = 0xf0 },
+ { .lomax = 35000, .pd = 0x5e, .d = 0xe0 },
+ { .lomax = 37000, .pd = 0x5d, .d = 0xd0 },
+ { .lomax = 41000, .pd = 0x5c, .d = 0xc0 },
+ { .lomax = 44000, .pd = 0x5b, .d = 0xb0 },
+ { .lomax = 49000, .pd = 0x5a, .d = 0xa0 },
+ { .lomax = 54000, .pd = 0x59, .d = 0x90 },
+ { .lomax = 61000, .pd = 0x58, .d = 0x80 },
+ { .lomax = 65000, .pd = 0x4f, .d = 0x78 },
+ { .lomax = 70000, .pd = 0x4e, .d = 0x70 },
+ { .lomax = 75000, .pd = 0x4d, .d = 0x68 },
+ { .lomax = 82000, .pd = 0x4c, .d = 0x60 },
+ { .lomax = 89000, .pd = 0x4b, .d = 0x58 },
+ { .lomax = 98000, .pd = 0x4a, .d = 0x50 },
+ { .lomax = 109000, .pd = 0x49, .d = 0x48 },
+ { .lomax = 123000, .pd = 0x48, .d = 0x40 },
+ { .lomax = 131000, .pd = 0x3f, .d = 0x3c },
+ { .lomax = 141000, .pd = 0x3e, .d = 0x38 },
+ { .lomax = 151000, .pd = 0x3d, .d = 0x34 },
+ { .lomax = 164000, .pd = 0x3c, .d = 0x30 },
+ { .lomax = 179000, .pd = 0x3b, .d = 0x2c },
+ { .lomax = 197000, .pd = 0x3a, .d = 0x28 },
+ { .lomax = 219000, .pd = 0x39, .d = 0x24 },
+ { .lomax = 246000, .pd = 0x38, .d = 0x20 },
+ { .lomax = 263000, .pd = 0x2f, .d = 0x1e },
+ { .lomax = 282000, .pd = 0x2e, .d = 0x1c },
+ { .lomax = 303000, .pd = 0x2d, .d = 0x1a },
+ { .lomax = 329000, .pd = 0x2c, .d = 0x18 },
+ { .lomax = 359000, .pd = 0x2b, .d = 0x16 },
+ { .lomax = 395000, .pd = 0x2a, .d = 0x14 },
+ { .lomax = 438000, .pd = 0x29, .d = 0x12 },
+ { .lomax = 493000, .pd = 0x28, .d = 0x10 },
+ { .lomax = 526000, .pd = 0x1f, .d = 0x0f },
+ { .lomax = 564000, .pd = 0x1e, .d = 0x0e },
+ { .lomax = 607000, .pd = 0x1d, .d = 0x0d },
+ { .lomax = 658000, .pd = 0x1c, .d = 0x0c },
+ { .lomax = 718000, .pd = 0x1b, .d = 0x0b },
+ { .lomax = 790000, .pd = 0x1a, .d = 0x0a },
+ { .lomax = 877000, .pd = 0x19, .d = 0x09 },
+ { .lomax = 987000, .pd = 0x18, .d = 0x08 },
+ { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */
+};
+
+static struct tda18271_pll_map tda18271c2_main_pll[] = {
+ { .lomax = 33125, .pd = 0x57, .d = 0xf0 },
+ { .lomax = 35500, .pd = 0x56, .d = 0xe0 },
+ { .lomax = 38188, .pd = 0x55, .d = 0xd0 },
+ { .lomax = 41375, .pd = 0x54, .d = 0xc0 },
+ { .lomax = 45125, .pd = 0x53, .d = 0xb0 },
+ { .lomax = 49688, .pd = 0x52, .d = 0xa0 },
+ { .lomax = 55188, .pd = 0x51, .d = 0x90 },
+ { .lomax = 62125, .pd = 0x50, .d = 0x80 },
+ { .lomax = 66250, .pd = 0x47, .d = 0x78 },
+ { .lomax = 71000, .pd = 0x46, .d = 0x70 },
+ { .lomax = 76375, .pd = 0x45, .d = 0x68 },
+ { .lomax = 82750, .pd = 0x44, .d = 0x60 },
+ { .lomax = 90250, .pd = 0x43, .d = 0x58 },
+ { .lomax = 99375, .pd = 0x42, .d = 0x50 },
+ { .lomax = 110375, .pd = 0x41, .d = 0x48 },
+ { .lomax = 124250, .pd = 0x40, .d = 0x40 },
+ { .lomax = 132500, .pd = 0x37, .d = 0x3c },
+ { .lomax = 142000, .pd = 0x36, .d = 0x38 },
+ { .lomax = 152750, .pd = 0x35, .d = 0x34 },
+ { .lomax = 165500, .pd = 0x34, .d = 0x30 },
+ { .lomax = 180500, .pd = 0x33, .d = 0x2c },
+ { .lomax = 198750, .pd = 0x32, .d = 0x28 },
+ { .lomax = 220750, .pd = 0x31, .d = 0x24 },
+ { .lomax = 248500, .pd = 0x30, .d = 0x20 },
+ { .lomax = 265000, .pd = 0x27, .d = 0x1e },
+ { .lomax = 284000, .pd = 0x26, .d = 0x1c },
+ { .lomax = 305500, .pd = 0x25, .d = 0x1a },
+ { .lomax = 331000, .pd = 0x24, .d = 0x18 },
+ { .lomax = 361000, .pd = 0x23, .d = 0x16 },
+ { .lomax = 397500, .pd = 0x22, .d = 0x14 },
+ { .lomax = 441500, .pd = 0x21, .d = 0x12 },
+ { .lomax = 497000, .pd = 0x20, .d = 0x10 },
+ { .lomax = 530000, .pd = 0x17, .d = 0x0f },
+ { .lomax = 568000, .pd = 0x16, .d = 0x0e },
+ { .lomax = 611000, .pd = 0x15, .d = 0x0d },
+ { .lomax = 662000, .pd = 0x14, .d = 0x0c },
+ { .lomax = 722000, .pd = 0x13, .d = 0x0b },
+ { .lomax = 795000, .pd = 0x12, .d = 0x0a },
+ { .lomax = 883000, .pd = 0x11, .d = 0x09 },
+ { .lomax = 994000, .pd = 0x10, .d = 0x08 },
+ { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */
+};
+
+static struct tda18271_pll_map tda18271c1_cal_pll[] = {
+ { .lomax = 33000, .pd = 0xdd, .d = 0xd0 },
+ { .lomax = 36000, .pd = 0xdc, .d = 0xc0 },
+ { .lomax = 40000, .pd = 0xdb, .d = 0xb0 },
+ { .lomax = 44000, .pd = 0xda, .d = 0xa0 },
+ { .lomax = 49000, .pd = 0xd9, .d = 0x90 },
+ { .lomax = 55000, .pd = 0xd8, .d = 0x80 },
+ { .lomax = 63000, .pd = 0xd3, .d = 0x70 },
+ { .lomax = 67000, .pd = 0xcd, .d = 0x68 },
+ { .lomax = 73000, .pd = 0xcc, .d = 0x60 },
+ { .lomax = 80000, .pd = 0xcb, .d = 0x58 },
+ { .lomax = 88000, .pd = 0xca, .d = 0x50 },
+ { .lomax = 98000, .pd = 0xc9, .d = 0x48 },
+ { .lomax = 110000, .pd = 0xc8, .d = 0x40 },
+ { .lomax = 126000, .pd = 0xc3, .d = 0x38 },
+ { .lomax = 135000, .pd = 0xbd, .d = 0x34 },
+ { .lomax = 147000, .pd = 0xbc, .d = 0x30 },
+ { .lomax = 160000, .pd = 0xbb, .d = 0x2c },
+ { .lomax = 176000, .pd = 0xba, .d = 0x28 },
+ { .lomax = 196000, .pd = 0xb9, .d = 0x24 },
+ { .lomax = 220000, .pd = 0xb8, .d = 0x20 },
+ { .lomax = 252000, .pd = 0xb3, .d = 0x1c },
+ { .lomax = 271000, .pd = 0xad, .d = 0x1a },
+ { .lomax = 294000, .pd = 0xac, .d = 0x18 },
+ { .lomax = 321000, .pd = 0xab, .d = 0x16 },
+ { .lomax = 353000, .pd = 0xaa, .d = 0x14 },
+ { .lomax = 392000, .pd = 0xa9, .d = 0x12 },
+ { .lomax = 441000, .pd = 0xa8, .d = 0x10 },
+ { .lomax = 505000, .pd = 0xa3, .d = 0x0e },
+ { .lomax = 543000, .pd = 0x9d, .d = 0x0d },
+ { .lomax = 589000, .pd = 0x9c, .d = 0x0c },
+ { .lomax = 642000, .pd = 0x9b, .d = 0x0b },
+ { .lomax = 707000, .pd = 0x9a, .d = 0x0a },
+ { .lomax = 785000, .pd = 0x99, .d = 0x09 },
+ { .lomax = 883000, .pd = 0x98, .d = 0x08 },
+ { .lomax = 1010000, .pd = 0x93, .d = 0x07 },
+ { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */
+};
+
+static struct tda18271_pll_map tda18271c2_cal_pll[] = {
+ { .lomax = 33813, .pd = 0xdd, .d = 0xd0 },
+ { .lomax = 36625, .pd = 0xdc, .d = 0xc0 },
+ { .lomax = 39938, .pd = 0xdb, .d = 0xb0 },
+ { .lomax = 43938, .pd = 0xda, .d = 0xa0 },
+ { .lomax = 48813, .pd = 0xd9, .d = 0x90 },
+ { .lomax = 54938, .pd = 0xd8, .d = 0x80 },
+ { .lomax = 62813, .pd = 0xd3, .d = 0x70 },
+ { .lomax = 67625, .pd = 0xcd, .d = 0x68 },
+ { .lomax = 73250, .pd = 0xcc, .d = 0x60 },
+ { .lomax = 79875, .pd = 0xcb, .d = 0x58 },
+ { .lomax = 87875, .pd = 0xca, .d = 0x50 },
+ { .lomax = 97625, .pd = 0xc9, .d = 0x48 },
+ { .lomax = 109875, .pd = 0xc8, .d = 0x40 },
+ { .lomax = 125625, .pd = 0xc3, .d = 0x38 },
+ { .lomax = 135250, .pd = 0xbd, .d = 0x34 },
+ { .lomax = 146500, .pd = 0xbc, .d = 0x30 },
+ { .lomax = 159750, .pd = 0xbb, .d = 0x2c },
+ { .lomax = 175750, .pd = 0xba, .d = 0x28 },
+ { .lomax = 195250, .pd = 0xb9, .d = 0x24 },
+ { .lomax = 219750, .pd = 0xb8, .d = 0x20 },
+ { .lomax = 251250, .pd = 0xb3, .d = 0x1c },
+ { .lomax = 270500, .pd = 0xad, .d = 0x1a },
+ { .lomax = 293000, .pd = 0xac, .d = 0x18 },
+ { .lomax = 319500, .pd = 0xab, .d = 0x16 },
+ { .lomax = 351500, .pd = 0xaa, .d = 0x14 },
+ { .lomax = 390500, .pd = 0xa9, .d = 0x12 },
+ { .lomax = 439500, .pd = 0xa8, .d = 0x10 },
+ { .lomax = 502500, .pd = 0xa3, .d = 0x0e },
+ { .lomax = 541000, .pd = 0x9d, .d = 0x0d },
+ { .lomax = 586000, .pd = 0x9c, .d = 0x0c },
+ { .lomax = 639000, .pd = 0x9b, .d = 0x0b },
+ { .lomax = 703000, .pd = 0x9a, .d = 0x0a },
+ { .lomax = 781000, .pd = 0x99, .d = 0x09 },
+ { .lomax = 879000, .pd = 0x98, .d = 0x08 },
+ { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271_bp_filter[] = {
+ { .rfmax = 62000, .val = 0x00 },
+ { .rfmax = 84000, .val = 0x01 },
+ { .rfmax = 100000, .val = 0x02 },
+ { .rfmax = 140000, .val = 0x03 },
+ { .rfmax = 170000, .val = 0x04 },
+ { .rfmax = 180000, .val = 0x05 },
+ { .rfmax = 865000, .val = 0x06 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271c1_km[] = {
+ { .rfmax = 61100, .val = 0x74 },
+ { .rfmax = 350000, .val = 0x40 },
+ { .rfmax = 720000, .val = 0x30 },
+ { .rfmax = 865000, .val = 0x40 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271c2_km[] = {
+ { .rfmax = 47900, .val = 0x38 },
+ { .rfmax = 61100, .val = 0x44 },
+ { .rfmax = 350000, .val = 0x30 },
+ { .rfmax = 720000, .val = 0x24 },
+ { .rfmax = 865000, .val = 0x3c },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271_rf_band[] = {
+ { .rfmax = 47900, .val = 0x00 },
+ { .rfmax = 61100, .val = 0x01 },
+/* { .rfmax = 152600, .val = 0x02 }, */
+ { .rfmax = 121200, .val = 0x02 },
+ { .rfmax = 164700, .val = 0x03 },
+ { .rfmax = 203500, .val = 0x04 },
+ { .rfmax = 457800, .val = 0x05 },
+ { .rfmax = 865000, .val = 0x06 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271_gain_taper[] = {
+ { .rfmax = 45400, .val = 0x1f },
+ { .rfmax = 45800, .val = 0x1e },
+ { .rfmax = 46200, .val = 0x1d },
+ { .rfmax = 46700, .val = 0x1c },
+ { .rfmax = 47100, .val = 0x1b },
+ { .rfmax = 47500, .val = 0x1a },
+ { .rfmax = 47900, .val = 0x19 },
+ { .rfmax = 49600, .val = 0x17 },
+ { .rfmax = 51200, .val = 0x16 },
+ { .rfmax = 52900, .val = 0x15 },
+ { .rfmax = 54500, .val = 0x14 },
+ { .rfmax = 56200, .val = 0x13 },
+ { .rfmax = 57800, .val = 0x12 },
+ { .rfmax = 59500, .val = 0x11 },
+ { .rfmax = 61100, .val = 0x10 },
+ { .rfmax = 67600, .val = 0x0d },
+ { .rfmax = 74200, .val = 0x0c },
+ { .rfmax = 80700, .val = 0x0b },
+ { .rfmax = 87200, .val = 0x0a },
+ { .rfmax = 93800, .val = 0x09 },
+ { .rfmax = 100300, .val = 0x08 },
+ { .rfmax = 106900, .val = 0x07 },
+ { .rfmax = 113400, .val = 0x06 },
+ { .rfmax = 119900, .val = 0x05 },
+ { .rfmax = 126500, .val = 0x04 },
+ { .rfmax = 133000, .val = 0x03 },
+ { .rfmax = 139500, .val = 0x02 },
+ { .rfmax = 146100, .val = 0x01 },
+ { .rfmax = 152600, .val = 0x00 },
+ { .rfmax = 154300, .val = 0x1f },
+ { .rfmax = 156100, .val = 0x1e },
+ { .rfmax = 157800, .val = 0x1d },
+ { .rfmax = 159500, .val = 0x1c },
+ { .rfmax = 161200, .val = 0x1b },
+ { .rfmax = 163000, .val = 0x1a },
+ { .rfmax = 164700, .val = 0x19 },
+ { .rfmax = 170200, .val = 0x17 },
+ { .rfmax = 175800, .val = 0x16 },
+ { .rfmax = 181300, .val = 0x15 },
+ { .rfmax = 186900, .val = 0x14 },
+ { .rfmax = 192400, .val = 0x13 },
+ { .rfmax = 198000, .val = 0x12 },
+ { .rfmax = 203500, .val = 0x11 },
+ { .rfmax = 216200, .val = 0x14 },
+ { .rfmax = 228900, .val = 0x13 },
+ { .rfmax = 241600, .val = 0x12 },
+ { .rfmax = 254400, .val = 0x11 },
+ { .rfmax = 267100, .val = 0x10 },
+ { .rfmax = 279800, .val = 0x0f },
+ { .rfmax = 292500, .val = 0x0e },
+ { .rfmax = 305200, .val = 0x0d },
+ { .rfmax = 317900, .val = 0x0c },
+ { .rfmax = 330700, .val = 0x0b },
+ { .rfmax = 343400, .val = 0x0a },
+ { .rfmax = 356100, .val = 0x09 },
+ { .rfmax = 368800, .val = 0x08 },
+ { .rfmax = 381500, .val = 0x07 },
+ { .rfmax = 394200, .val = 0x06 },
+ { .rfmax = 406900, .val = 0x05 },
+ { .rfmax = 419700, .val = 0x04 },
+ { .rfmax = 432400, .val = 0x03 },
+ { .rfmax = 445100, .val = 0x02 },
+ { .rfmax = 457800, .val = 0x01 },
+ { .rfmax = 476300, .val = 0x19 },
+ { .rfmax = 494800, .val = 0x18 },
+ { .rfmax = 513300, .val = 0x17 },
+ { .rfmax = 531800, .val = 0x16 },
+ { .rfmax = 550300, .val = 0x15 },
+ { .rfmax = 568900, .val = 0x14 },
+ { .rfmax = 587400, .val = 0x13 },
+ { .rfmax = 605900, .val = 0x12 },
+ { .rfmax = 624400, .val = 0x11 },
+ { .rfmax = 642900, .val = 0x10 },
+ { .rfmax = 661400, .val = 0x0f },
+ { .rfmax = 679900, .val = 0x0e },
+ { .rfmax = 698400, .val = 0x0d },
+ { .rfmax = 716900, .val = 0x0c },
+ { .rfmax = 735400, .val = 0x0b },
+ { .rfmax = 753900, .val = 0x0a },
+ { .rfmax = 772500, .val = 0x09 },
+ { .rfmax = 791000, .val = 0x08 },
+ { .rfmax = 809500, .val = 0x07 },
+ { .rfmax = 828000, .val = 0x06 },
+ { .rfmax = 846500, .val = 0x05 },
+ { .rfmax = 865000, .val = 0x04 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271c1_rf_cal[] = {
+ { .rfmax = 41000, .val = 0x1e },
+ { .rfmax = 43000, .val = 0x30 },
+ { .rfmax = 45000, .val = 0x43 },
+ { .rfmax = 46000, .val = 0x4d },
+ { .rfmax = 47000, .val = 0x54 },
+ { .rfmax = 47900, .val = 0x64 },
+ { .rfmax = 49100, .val = 0x20 },
+ { .rfmax = 50000, .val = 0x22 },
+ { .rfmax = 51000, .val = 0x2a },
+ { .rfmax = 53000, .val = 0x32 },
+ { .rfmax = 55000, .val = 0x35 },
+ { .rfmax = 56000, .val = 0x3c },
+ { .rfmax = 57000, .val = 0x3f },
+ { .rfmax = 58000, .val = 0x48 },
+ { .rfmax = 59000, .val = 0x4d },
+ { .rfmax = 60000, .val = 0x58 },
+ { .rfmax = 61100, .val = 0x5f },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271c2_rf_cal[] = {
+ { .rfmax = 41000, .val = 0x0f },
+ { .rfmax = 43000, .val = 0x1c },
+ { .rfmax = 45000, .val = 0x2f },
+ { .rfmax = 46000, .val = 0x39 },
+ { .rfmax = 47000, .val = 0x40 },
+ { .rfmax = 47900, .val = 0x50 },
+ { .rfmax = 49100, .val = 0x16 },
+ { .rfmax = 50000, .val = 0x18 },
+ { .rfmax = 51000, .val = 0x20 },
+ { .rfmax = 53000, .val = 0x28 },
+ { .rfmax = 55000, .val = 0x2b },
+ { .rfmax = 56000, .val = 0x32 },
+ { .rfmax = 57000, .val = 0x35 },
+ { .rfmax = 58000, .val = 0x3e },
+ { .rfmax = 59000, .val = 0x43 },
+ { .rfmax = 60000, .val = 0x4e },
+ { .rfmax = 61100, .val = 0x55 },
+ { .rfmax = 63000, .val = 0x0f },
+ { .rfmax = 64000, .val = 0x11 },
+ { .rfmax = 65000, .val = 0x12 },
+ { .rfmax = 66000, .val = 0x15 },
+ { .rfmax = 67000, .val = 0x16 },
+ { .rfmax = 68000, .val = 0x17 },
+ { .rfmax = 70000, .val = 0x19 },
+ { .rfmax = 71000, .val = 0x1c },
+ { .rfmax = 72000, .val = 0x1d },
+ { .rfmax = 73000, .val = 0x1f },
+ { .rfmax = 74000, .val = 0x20 },
+ { .rfmax = 75000, .val = 0x21 },
+ { .rfmax = 76000, .val = 0x24 },
+ { .rfmax = 77000, .val = 0x25 },
+ { .rfmax = 78000, .val = 0x27 },
+ { .rfmax = 80000, .val = 0x28 },
+ { .rfmax = 81000, .val = 0x29 },
+ { .rfmax = 82000, .val = 0x2d },
+ { .rfmax = 83000, .val = 0x2e },
+ { .rfmax = 84000, .val = 0x2f },
+ { .rfmax = 85000, .val = 0x31 },
+ { .rfmax = 86000, .val = 0x33 },
+ { .rfmax = 87000, .val = 0x34 },
+ { .rfmax = 88000, .val = 0x35 },
+ { .rfmax = 89000, .val = 0x37 },
+ { .rfmax = 90000, .val = 0x38 },
+ { .rfmax = 91000, .val = 0x39 },
+ { .rfmax = 93000, .val = 0x3c },
+ { .rfmax = 94000, .val = 0x3e },
+ { .rfmax = 95000, .val = 0x3f },
+ { .rfmax = 96000, .val = 0x40 },
+ { .rfmax = 97000, .val = 0x42 },
+ { .rfmax = 99000, .val = 0x45 },
+ { .rfmax = 100000, .val = 0x46 },
+ { .rfmax = 102000, .val = 0x48 },
+ { .rfmax = 103000, .val = 0x4a },
+ { .rfmax = 105000, .val = 0x4d },
+ { .rfmax = 106000, .val = 0x4e },
+ { .rfmax = 107000, .val = 0x50 },
+ { .rfmax = 108000, .val = 0x51 },
+ { .rfmax = 110000, .val = 0x54 },
+ { .rfmax = 111000, .val = 0x56 },
+ { .rfmax = 112000, .val = 0x57 },
+ { .rfmax = 113000, .val = 0x58 },
+ { .rfmax = 114000, .val = 0x59 },
+ { .rfmax = 115000, .val = 0x5c },
+ { .rfmax = 116000, .val = 0x5d },
+ { .rfmax = 117000, .val = 0x5f },
+ { .rfmax = 119000, .val = 0x60 },
+ { .rfmax = 120000, .val = 0x64 },
+ { .rfmax = 121000, .val = 0x65 },
+ { .rfmax = 122000, .val = 0x66 },
+ { .rfmax = 123000, .val = 0x68 },
+ { .rfmax = 124000, .val = 0x69 },
+ { .rfmax = 125000, .val = 0x6c },
+ { .rfmax = 126000, .val = 0x6d },
+ { .rfmax = 127000, .val = 0x6e },
+ { .rfmax = 128000, .val = 0x70 },
+ { .rfmax = 129000, .val = 0x71 },
+ { .rfmax = 130000, .val = 0x75 },
+ { .rfmax = 131000, .val = 0x77 },
+ { .rfmax = 132000, .val = 0x78 },
+ { .rfmax = 133000, .val = 0x7b },
+ { .rfmax = 134000, .val = 0x7e },
+ { .rfmax = 135000, .val = 0x81 },
+ { .rfmax = 136000, .val = 0x82 },
+ { .rfmax = 137000, .val = 0x87 },
+ { .rfmax = 138000, .val = 0x88 },
+ { .rfmax = 139000, .val = 0x8d },
+ { .rfmax = 140000, .val = 0x8e },
+ { .rfmax = 141000, .val = 0x91 },
+ { .rfmax = 142000, .val = 0x95 },
+ { .rfmax = 143000, .val = 0x9a },
+ { .rfmax = 144000, .val = 0x9d },
+ { .rfmax = 145000, .val = 0xa1 },
+ { .rfmax = 146000, .val = 0xa2 },
+ { .rfmax = 147000, .val = 0xa4 },
+ { .rfmax = 148000, .val = 0xa9 },
+ { .rfmax = 149000, .val = 0xae },
+ { .rfmax = 150000, .val = 0xb0 },
+ { .rfmax = 151000, .val = 0xb1 },
+ { .rfmax = 152000, .val = 0xb7 },
+ { .rfmax = 153000, .val = 0xbd },
+ { .rfmax = 154000, .val = 0x20 },
+ { .rfmax = 155000, .val = 0x22 },
+ { .rfmax = 156000, .val = 0x24 },
+ { .rfmax = 157000, .val = 0x25 },
+ { .rfmax = 158000, .val = 0x27 },
+ { .rfmax = 159000, .val = 0x29 },
+ { .rfmax = 160000, .val = 0x2c },
+ { .rfmax = 161000, .val = 0x2d },
+ { .rfmax = 163000, .val = 0x2e },
+ { .rfmax = 164000, .val = 0x2f },
+ { .rfmax = 165000, .val = 0x30 },
+ { .rfmax = 166000, .val = 0x11 },
+ { .rfmax = 167000, .val = 0x12 },
+ { .rfmax = 168000, .val = 0x13 },
+ { .rfmax = 169000, .val = 0x14 },
+ { .rfmax = 170000, .val = 0x15 },
+ { .rfmax = 172000, .val = 0x16 },
+ { .rfmax = 173000, .val = 0x17 },
+ { .rfmax = 174000, .val = 0x18 },
+ { .rfmax = 175000, .val = 0x1a },
+ { .rfmax = 176000, .val = 0x1b },
+ { .rfmax = 178000, .val = 0x1d },
+ { .rfmax = 179000, .val = 0x1e },
+ { .rfmax = 180000, .val = 0x1f },
+ { .rfmax = 181000, .val = 0x20 },
+ { .rfmax = 182000, .val = 0x21 },
+ { .rfmax = 183000, .val = 0x22 },
+ { .rfmax = 184000, .val = 0x24 },
+ { .rfmax = 185000, .val = 0x25 },
+ { .rfmax = 186000, .val = 0x26 },
+ { .rfmax = 187000, .val = 0x27 },
+ { .rfmax = 188000, .val = 0x29 },
+ { .rfmax = 189000, .val = 0x2a },
+ { .rfmax = 190000, .val = 0x2c },
+ { .rfmax = 191000, .val = 0x2d },
+ { .rfmax = 192000, .val = 0x2e },
+ { .rfmax = 193000, .val = 0x2f },
+ { .rfmax = 194000, .val = 0x30 },
+ { .rfmax = 195000, .val = 0x33 },
+ { .rfmax = 196000, .val = 0x35 },
+ { .rfmax = 198000, .val = 0x36 },
+ { .rfmax = 200000, .val = 0x38 },
+ { .rfmax = 201000, .val = 0x3c },
+ { .rfmax = 202000, .val = 0x3d },
+ { .rfmax = 203500, .val = 0x3e },
+ { .rfmax = 206000, .val = 0x0e },
+ { .rfmax = 208000, .val = 0x0f },
+ { .rfmax = 212000, .val = 0x10 },
+ { .rfmax = 216000, .val = 0x11 },
+ { .rfmax = 217000, .val = 0x12 },
+ { .rfmax = 218000, .val = 0x13 },
+ { .rfmax = 220000, .val = 0x14 },
+ { .rfmax = 222000, .val = 0x15 },
+ { .rfmax = 225000, .val = 0x16 },
+ { .rfmax = 228000, .val = 0x17 },
+ { .rfmax = 231000, .val = 0x18 },
+ { .rfmax = 234000, .val = 0x19 },
+ { .rfmax = 235000, .val = 0x1a },
+ { .rfmax = 236000, .val = 0x1b },
+ { .rfmax = 237000, .val = 0x1c },
+ { .rfmax = 240000, .val = 0x1d },
+ { .rfmax = 242000, .val = 0x1f },
+ { .rfmax = 247000, .val = 0x20 },
+ { .rfmax = 249000, .val = 0x21 },
+ { .rfmax = 252000, .val = 0x22 },
+ { .rfmax = 253000, .val = 0x23 },
+ { .rfmax = 254000, .val = 0x24 },
+ { .rfmax = 256000, .val = 0x25 },
+ { .rfmax = 259000, .val = 0x26 },
+ { .rfmax = 262000, .val = 0x27 },
+ { .rfmax = 264000, .val = 0x28 },
+ { .rfmax = 267000, .val = 0x29 },
+ { .rfmax = 269000, .val = 0x2a },
+ { .rfmax = 271000, .val = 0x2b },
+ { .rfmax = 273000, .val = 0x2c },
+ { .rfmax = 275000, .val = 0x2d },
+ { .rfmax = 277000, .val = 0x2e },
+ { .rfmax = 279000, .val = 0x2f },
+ { .rfmax = 282000, .val = 0x30 },
+ { .rfmax = 284000, .val = 0x31 },
+ { .rfmax = 286000, .val = 0x32 },
+ { .rfmax = 287000, .val = 0x33 },
+ { .rfmax = 290000, .val = 0x34 },
+ { .rfmax = 293000, .val = 0x35 },
+ { .rfmax = 295000, .val = 0x36 },
+ { .rfmax = 297000, .val = 0x37 },
+ { .rfmax = 300000, .val = 0x38 },
+ { .rfmax = 303000, .val = 0x39 },
+ { .rfmax = 305000, .val = 0x3a },
+ { .rfmax = 306000, .val = 0x3b },
+ { .rfmax = 307000, .val = 0x3c },
+ { .rfmax = 310000, .val = 0x3d },
+ { .rfmax = 312000, .val = 0x3e },
+ { .rfmax = 315000, .val = 0x3f },
+ { .rfmax = 318000, .val = 0x40 },
+ { .rfmax = 320000, .val = 0x41 },
+ { .rfmax = 323000, .val = 0x42 },
+ { .rfmax = 324000, .val = 0x43 },
+ { .rfmax = 325000, .val = 0x44 },
+ { .rfmax = 327000, .val = 0x45 },
+ { .rfmax = 331000, .val = 0x46 },
+ { .rfmax = 334000, .val = 0x47 },
+ { .rfmax = 337000, .val = 0x48 },
+ { .rfmax = 339000, .val = 0x49 },
+ { .rfmax = 340000, .val = 0x4a },
+ { .rfmax = 341000, .val = 0x4b },
+ { .rfmax = 343000, .val = 0x4c },
+ { .rfmax = 345000, .val = 0x4d },
+ { .rfmax = 349000, .val = 0x4e },
+ { .rfmax = 352000, .val = 0x4f },
+ { .rfmax = 353000, .val = 0x50 },
+ { .rfmax = 355000, .val = 0x51 },
+ { .rfmax = 357000, .val = 0x52 },
+ { .rfmax = 359000, .val = 0x53 },
+ { .rfmax = 361000, .val = 0x54 },
+ { .rfmax = 362000, .val = 0x55 },
+ { .rfmax = 364000, .val = 0x56 },
+ { .rfmax = 368000, .val = 0x57 },
+ { .rfmax = 370000, .val = 0x58 },
+ { .rfmax = 372000, .val = 0x59 },
+ { .rfmax = 375000, .val = 0x5a },
+ { .rfmax = 376000, .val = 0x5b },
+ { .rfmax = 377000, .val = 0x5c },
+ { .rfmax = 379000, .val = 0x5d },
+ { .rfmax = 382000, .val = 0x5e },
+ { .rfmax = 384000, .val = 0x5f },
+ { .rfmax = 385000, .val = 0x60 },
+ { .rfmax = 386000, .val = 0x61 },
+ { .rfmax = 388000, .val = 0x62 },
+ { .rfmax = 390000, .val = 0x63 },
+ { .rfmax = 393000, .val = 0x64 },
+ { .rfmax = 394000, .val = 0x65 },
+ { .rfmax = 396000, .val = 0x66 },
+ { .rfmax = 397000, .val = 0x67 },
+ { .rfmax = 398000, .val = 0x68 },
+ { .rfmax = 400000, .val = 0x69 },
+ { .rfmax = 402000, .val = 0x6a },
+ { .rfmax = 403000, .val = 0x6b },
+ { .rfmax = 407000, .val = 0x6c },
+ { .rfmax = 408000, .val = 0x6d },
+ { .rfmax = 409000, .val = 0x6e },
+ { .rfmax = 410000, .val = 0x6f },
+ { .rfmax = 411000, .val = 0x70 },
+ { .rfmax = 412000, .val = 0x71 },
+ { .rfmax = 413000, .val = 0x72 },
+ { .rfmax = 414000, .val = 0x73 },
+ { .rfmax = 417000, .val = 0x74 },
+ { .rfmax = 418000, .val = 0x75 },
+ { .rfmax = 420000, .val = 0x76 },
+ { .rfmax = 422000, .val = 0x77 },
+ { .rfmax = 423000, .val = 0x78 },
+ { .rfmax = 424000, .val = 0x79 },
+ { .rfmax = 427000, .val = 0x7a },
+ { .rfmax = 428000, .val = 0x7b },
+ { .rfmax = 429000, .val = 0x7d },
+ { .rfmax = 432000, .val = 0x7f },
+ { .rfmax = 434000, .val = 0x80 },
+ { .rfmax = 435000, .val = 0x81 },
+ { .rfmax = 436000, .val = 0x83 },
+ { .rfmax = 437000, .val = 0x84 },
+ { .rfmax = 438000, .val = 0x85 },
+ { .rfmax = 439000, .val = 0x86 },
+ { .rfmax = 440000, .val = 0x87 },
+ { .rfmax = 441000, .val = 0x88 },
+ { .rfmax = 442000, .val = 0x89 },
+ { .rfmax = 445000, .val = 0x8a },
+ { .rfmax = 446000, .val = 0x8b },
+ { .rfmax = 447000, .val = 0x8c },
+ { .rfmax = 448000, .val = 0x8e },
+ { .rfmax = 449000, .val = 0x8f },
+ { .rfmax = 450000, .val = 0x90 },
+ { .rfmax = 452000, .val = 0x91 },
+ { .rfmax = 453000, .val = 0x93 },
+ { .rfmax = 454000, .val = 0x94 },
+ { .rfmax = 456000, .val = 0x96 },
+ { .rfmax = 457000, .val = 0x98 },
+ { .rfmax = 461000, .val = 0x11 },
+ { .rfmax = 468000, .val = 0x12 },
+ { .rfmax = 472000, .val = 0x13 },
+ { .rfmax = 473000, .val = 0x14 },
+ { .rfmax = 474000, .val = 0x15 },
+ { .rfmax = 481000, .val = 0x16 },
+ { .rfmax = 486000, .val = 0x17 },
+ { .rfmax = 491000, .val = 0x18 },
+ { .rfmax = 498000, .val = 0x19 },
+ { .rfmax = 499000, .val = 0x1a },
+ { .rfmax = 501000, .val = 0x1b },
+ { .rfmax = 506000, .val = 0x1c },
+ { .rfmax = 511000, .val = 0x1d },
+ { .rfmax = 516000, .val = 0x1e },
+ { .rfmax = 520000, .val = 0x1f },
+ { .rfmax = 521000, .val = 0x20 },
+ { .rfmax = 525000, .val = 0x21 },
+ { .rfmax = 529000, .val = 0x22 },
+ { .rfmax = 533000, .val = 0x23 },
+ { .rfmax = 539000, .val = 0x24 },
+ { .rfmax = 541000, .val = 0x25 },
+ { .rfmax = 547000, .val = 0x26 },
+ { .rfmax = 549000, .val = 0x27 },
+ { .rfmax = 551000, .val = 0x28 },
+ { .rfmax = 556000, .val = 0x29 },
+ { .rfmax = 561000, .val = 0x2a },
+ { .rfmax = 563000, .val = 0x2b },
+ { .rfmax = 565000, .val = 0x2c },
+ { .rfmax = 569000, .val = 0x2d },
+ { .rfmax = 571000, .val = 0x2e },
+ { .rfmax = 577000, .val = 0x2f },
+ { .rfmax = 580000, .val = 0x30 },
+ { .rfmax = 582000, .val = 0x31 },
+ { .rfmax = 584000, .val = 0x32 },
+ { .rfmax = 588000, .val = 0x33 },
+ { .rfmax = 591000, .val = 0x34 },
+ { .rfmax = 596000, .val = 0x35 },
+ { .rfmax = 598000, .val = 0x36 },
+ { .rfmax = 603000, .val = 0x37 },
+ { .rfmax = 604000, .val = 0x38 },
+ { .rfmax = 606000, .val = 0x39 },
+ { .rfmax = 612000, .val = 0x3a },
+ { .rfmax = 615000, .val = 0x3b },
+ { .rfmax = 617000, .val = 0x3c },
+ { .rfmax = 621000, .val = 0x3d },
+ { .rfmax = 622000, .val = 0x3e },
+ { .rfmax = 625000, .val = 0x3f },
+ { .rfmax = 632000, .val = 0x40 },
+ { .rfmax = 633000, .val = 0x41 },
+ { .rfmax = 634000, .val = 0x42 },
+ { .rfmax = 642000, .val = 0x43 },
+ { .rfmax = 643000, .val = 0x44 },
+ { .rfmax = 647000, .val = 0x45 },
+ { .rfmax = 650000, .val = 0x46 },
+ { .rfmax = 652000, .val = 0x47 },
+ { .rfmax = 657000, .val = 0x48 },
+ { .rfmax = 661000, .val = 0x49 },
+ { .rfmax = 662000, .val = 0x4a },
+ { .rfmax = 665000, .val = 0x4b },
+ { .rfmax = 667000, .val = 0x4c },
+ { .rfmax = 670000, .val = 0x4d },
+ { .rfmax = 673000, .val = 0x4e },
+ { .rfmax = 676000, .val = 0x4f },
+ { .rfmax = 677000, .val = 0x50 },
+ { .rfmax = 681000, .val = 0x51 },
+ { .rfmax = 683000, .val = 0x52 },
+ { .rfmax = 686000, .val = 0x53 },
+ { .rfmax = 688000, .val = 0x54 },
+ { .rfmax = 689000, .val = 0x55 },
+ { .rfmax = 691000, .val = 0x56 },
+ { .rfmax = 695000, .val = 0x57 },
+ { .rfmax = 698000, .val = 0x58 },
+ { .rfmax = 703000, .val = 0x59 },
+ { .rfmax = 704000, .val = 0x5a },
+ { .rfmax = 705000, .val = 0x5b },
+ { .rfmax = 707000, .val = 0x5c },
+ { .rfmax = 710000, .val = 0x5d },
+ { .rfmax = 712000, .val = 0x5e },
+ { .rfmax = 717000, .val = 0x5f },
+ { .rfmax = 718000, .val = 0x60 },
+ { .rfmax = 721000, .val = 0x61 },
+ { .rfmax = 722000, .val = 0x62 },
+ { .rfmax = 723000, .val = 0x63 },
+ { .rfmax = 725000, .val = 0x64 },
+ { .rfmax = 727000, .val = 0x65 },
+ { .rfmax = 730000, .val = 0x66 },
+ { .rfmax = 732000, .val = 0x67 },
+ { .rfmax = 735000, .val = 0x68 },
+ { .rfmax = 740000, .val = 0x69 },
+ { .rfmax = 741000, .val = 0x6a },
+ { .rfmax = 742000, .val = 0x6b },
+ { .rfmax = 743000, .val = 0x6c },
+ { .rfmax = 745000, .val = 0x6d },
+ { .rfmax = 747000, .val = 0x6e },
+ { .rfmax = 748000, .val = 0x6f },
+ { .rfmax = 750000, .val = 0x70 },
+ { .rfmax = 752000, .val = 0x71 },
+ { .rfmax = 754000, .val = 0x72 },
+ { .rfmax = 757000, .val = 0x73 },
+ { .rfmax = 758000, .val = 0x74 },
+ { .rfmax = 760000, .val = 0x75 },
+ { .rfmax = 763000, .val = 0x76 },
+ { .rfmax = 764000, .val = 0x77 },
+ { .rfmax = 766000, .val = 0x78 },
+ { .rfmax = 767000, .val = 0x79 },
+ { .rfmax = 768000, .val = 0x7a },
+ { .rfmax = 773000, .val = 0x7b },
+ { .rfmax = 774000, .val = 0x7c },
+ { .rfmax = 776000, .val = 0x7d },
+ { .rfmax = 777000, .val = 0x7e },
+ { .rfmax = 778000, .val = 0x7f },
+ { .rfmax = 779000, .val = 0x80 },
+ { .rfmax = 781000, .val = 0x81 },
+ { .rfmax = 783000, .val = 0x82 },
+ { .rfmax = 784000, .val = 0x83 },
+ { .rfmax = 785000, .val = 0x84 },
+ { .rfmax = 786000, .val = 0x85 },
+ { .rfmax = 793000, .val = 0x86 },
+ { .rfmax = 794000, .val = 0x87 },
+ { .rfmax = 795000, .val = 0x88 },
+ { .rfmax = 797000, .val = 0x89 },
+ { .rfmax = 799000, .val = 0x8a },
+ { .rfmax = 801000, .val = 0x8b },
+ { .rfmax = 802000, .val = 0x8c },
+ { .rfmax = 803000, .val = 0x8d },
+ { .rfmax = 804000, .val = 0x8e },
+ { .rfmax = 810000, .val = 0x90 },
+ { .rfmax = 811000, .val = 0x91 },
+ { .rfmax = 812000, .val = 0x92 },
+ { .rfmax = 814000, .val = 0x93 },
+ { .rfmax = 816000, .val = 0x94 },
+ { .rfmax = 817000, .val = 0x96 },
+ { .rfmax = 818000, .val = 0x97 },
+ { .rfmax = 820000, .val = 0x98 },
+ { .rfmax = 821000, .val = 0x99 },
+ { .rfmax = 822000, .val = 0x9a },
+ { .rfmax = 828000, .val = 0x9b },
+ { .rfmax = 829000, .val = 0x9d },
+ { .rfmax = 830000, .val = 0x9f },
+ { .rfmax = 831000, .val = 0xa0 },
+ { .rfmax = 833000, .val = 0xa1 },
+ { .rfmax = 835000, .val = 0xa2 },
+ { .rfmax = 836000, .val = 0xa3 },
+ { .rfmax = 837000, .val = 0xa4 },
+ { .rfmax = 838000, .val = 0xa6 },
+ { .rfmax = 840000, .val = 0xa8 },
+ { .rfmax = 842000, .val = 0xa9 },
+ { .rfmax = 845000, .val = 0xaa },
+ { .rfmax = 846000, .val = 0xab },
+ { .rfmax = 847000, .val = 0xad },
+ { .rfmax = 848000, .val = 0xae },
+ { .rfmax = 852000, .val = 0xaf },
+ { .rfmax = 853000, .val = 0xb0 },
+ { .rfmax = 858000, .val = 0xb1 },
+ { .rfmax = 860000, .val = 0xb2 },
+ { .rfmax = 861000, .val = 0xb3 },
+ { .rfmax = 862000, .val = 0xb4 },
+ { .rfmax = 863000, .val = 0xb6 },
+ { .rfmax = 864000, .val = 0xb8 },
+ { .rfmax = 865000, .val = 0xb9 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271_ir_measure[] = {
+ { .rfmax = 30000, .val = 4 },
+ { .rfmax = 200000, .val = 5 },
+ { .rfmax = 600000, .val = 6 },
+ { .rfmax = 865000, .val = 7 },
+ { .rfmax = 0, .val = 0 }, /* end */
+};
+
+static struct tda18271_map tda18271_rf_cal_dc_over_dt[] = {
+ { .rfmax = 47900, .val = 0x00 },
+ { .rfmax = 55000, .val = 0x00 },
+ { .rfmax = 61100, .val = 0x0a },
+ { .rfmax = 64000, .val = 0x0a },
+ { .rfmax = 82000, .val = 0x14 },
+ { .rfmax = 84000, .val = 0x19 },
+ { .rfmax = 119000, .val = 0x1c },
+ { .rfmax = 124000, .val = 0x20 },
+ { .rfmax = 129000, .val = 0x2a },
+ { .rfmax = 134000, .val = 0x32 },
+ { .rfmax = 139000, .val = 0x39 },
+ { .rfmax = 144000, .val = 0x3e },
+ { .rfmax = 149000, .val = 0x3f },
+ { .rfmax = 152600, .val = 0x40 },
+ { .rfmax = 154000, .val = 0x40 },
+ { .rfmax = 164700, .val = 0x41 },
+ { .rfmax = 203500, .val = 0x32 },
+ { .rfmax = 353000, .val = 0x19 },
+ { .rfmax = 356000, .val = 0x1a },
+ { .rfmax = 359000, .val = 0x1b },
+ { .rfmax = 363000, .val = 0x1c },
+ { .rfmax = 366000, .val = 0x1d },
+ { .rfmax = 369000, .val = 0x1e },
+ { .rfmax = 373000, .val = 0x1f },
+ { .rfmax = 376000, .val = 0x20 },
+ { .rfmax = 379000, .val = 0x21 },
+ { .rfmax = 383000, .val = 0x22 },
+ { .rfmax = 386000, .val = 0x23 },
+ { .rfmax = 389000, .val = 0x24 },
+ { .rfmax = 393000, .val = 0x25 },
+ { .rfmax = 396000, .val = 0x26 },
+ { .rfmax = 399000, .val = 0x27 },
+ { .rfmax = 402000, .val = 0x28 },
+ { .rfmax = 404000, .val = 0x29 },
+ { .rfmax = 407000, .val = 0x2a },
+ { .rfmax = 409000, .val = 0x2b },
+ { .rfmax = 412000, .val = 0x2c },
+ { .rfmax = 414000, .val = 0x2d },
+ { .rfmax = 417000, .val = 0x2e },
+ { .rfmax = 419000, .val = 0x2f },
+ { .rfmax = 422000, .val = 0x30 },
+ { .rfmax = 424000, .val = 0x31 },
+ { .rfmax = 427000, .val = 0x32 },
+ { .rfmax = 429000, .val = 0x33 },
+ { .rfmax = 432000, .val = 0x34 },
+ { .rfmax = 434000, .val = 0x35 },
+ { .rfmax = 437000, .val = 0x36 },
+ { .rfmax = 439000, .val = 0x37 },
+ { .rfmax = 442000, .val = 0x38 },
+ { .rfmax = 444000, .val = 0x39 },
+ { .rfmax = 447000, .val = 0x3a },
+ { .rfmax = 449000, .val = 0x3b },
+ { .rfmax = 457800, .val = 0x3c },
+ { .rfmax = 465000, .val = 0x0f },
+ { .rfmax = 477000, .val = 0x12 },
+ { .rfmax = 483000, .val = 0x14 },
+ { .rfmax = 502000, .val = 0x19 },
+ { .rfmax = 508000, .val = 0x1b },
+ { .rfmax = 519000, .val = 0x1c },
+ { .rfmax = 522000, .val = 0x1d },
+ { .rfmax = 524000, .val = 0x1e },
+ { .rfmax = 534000, .val = 0x1f },
+ { .rfmax = 549000, .val = 0x20 },
+ { .rfmax = 554000, .val = 0x22 },
+ { .rfmax = 584000, .val = 0x24 },
+ { .rfmax = 589000, .val = 0x26 },
+ { .rfmax = 658000, .val = 0x27 },
+ { .rfmax = 664000, .val = 0x2c },
+ { .rfmax = 669000, .val = 0x2d },
+ { .rfmax = 699000, .val = 0x2e },
+ { .rfmax = 704000, .val = 0x30 },
+ { .rfmax = 709000, .val = 0x31 },
+ { .rfmax = 714000, .val = 0x32 },
+ { .rfmax = 724000, .val = 0x33 },
+ { .rfmax = 729000, .val = 0x36 },
+ { .rfmax = 739000, .val = 0x38 },
+ { .rfmax = 744000, .val = 0x39 },
+ { .rfmax = 749000, .val = 0x3b },
+ { .rfmax = 754000, .val = 0x3c },
+ { .rfmax = 759000, .val = 0x3d },
+ { .rfmax = 764000, .val = 0x3e },
+ { .rfmax = 769000, .val = 0x3f },
+ { .rfmax = 774000, .val = 0x40 },
+ { .rfmax = 779000, .val = 0x41 },
+ { .rfmax = 784000, .val = 0x43 },
+ { .rfmax = 789000, .val = 0x46 },
+ { .rfmax = 794000, .val = 0x48 },
+ { .rfmax = 799000, .val = 0x4b },
+ { .rfmax = 804000, .val = 0x4f },
+ { .rfmax = 809000, .val = 0x54 },
+ { .rfmax = 814000, .val = 0x59 },
+ { .rfmax = 819000, .val = 0x5d },
+ { .rfmax = 824000, .val = 0x61 },
+ { .rfmax = 829000, .val = 0x68 },
+ { .rfmax = 834000, .val = 0x6e },
+ { .rfmax = 839000, .val = 0x75 },
+ { .rfmax = 844000, .val = 0x7e },
+ { .rfmax = 849000, .val = 0x82 },
+ { .rfmax = 854000, .val = 0x84 },
+ { .rfmax = 859000, .val = 0x8f },
+ { .rfmax = 865000, .val = 0x9a },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+/*---------------------------------------------------------------------*/
+
+struct tda18271_thermo_map {
+ u8 d;
+ u8 r0;
+ u8 r1;
+};
+
+static struct tda18271_thermo_map tda18271_thermometer[] = {
+ { .d = 0x00, .r0 = 60, .r1 = 92 },
+ { .d = 0x01, .r0 = 62, .r1 = 94 },
+ { .d = 0x02, .r0 = 66, .r1 = 98 },
+ { .d = 0x03, .r0 = 64, .r1 = 96 },
+ { .d = 0x04, .r0 = 74, .r1 = 106 },
+ { .d = 0x05, .r0 = 72, .r1 = 104 },
+ { .d = 0x06, .r0 = 68, .r1 = 100 },
+ { .d = 0x07, .r0 = 70, .r1 = 102 },
+ { .d = 0x08, .r0 = 90, .r1 = 122 },
+ { .d = 0x09, .r0 = 88, .r1 = 120 },
+ { .d = 0x0a, .r0 = 84, .r1 = 116 },
+ { .d = 0x0b, .r0 = 86, .r1 = 118 },
+ { .d = 0x0c, .r0 = 76, .r1 = 108 },
+ { .d = 0x0d, .r0 = 78, .r1 = 110 },
+ { .d = 0x0e, .r0 = 82, .r1 = 114 },
+ { .d = 0x0f, .r0 = 80, .r1 = 112 },
+ { .d = 0x00, .r0 = 0, .r1 = 0 }, /* end */
+};
+
+int tda18271_lookup_thermometer(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int val, i = 0;
+
+ while (tda18271_thermometer[i].d < (regs[R_TM] & 0x0f)) {
+ if (tda18271_thermometer[i + 1].d == 0)
+ break;
+ i++;
+ }
+
+ if ((regs[R_TM] & 0x20) == 0x20)
+ val = tda18271_thermometer[i].r1;
+ else
+ val = tda18271_thermometer[i].r0;
+
+ tda_map("(%d) tm = %d\n", i, val);
+
+ return val;
+}
+
+/*---------------------------------------------------------------------*/
+
+struct tda18271_cid_target_map {
+ u32 rfmax;
+ u8 target;
+ u16 limit;
+};
+
+static struct tda18271_cid_target_map tda18271_cid_target[] = {
+ { .rfmax = 46000, .target = 0x04, .limit = 1800 },
+ { .rfmax = 52200, .target = 0x0a, .limit = 1500 },
+ { .rfmax = 79100, .target = 0x01, .limit = 4000 },
+ { .rfmax = 136800, .target = 0x18, .limit = 4000 },
+ { .rfmax = 156700, .target = 0x18, .limit = 4000 },
+ { .rfmax = 156700, .target = 0x18, .limit = 4000 },
+ { .rfmax = 186250, .target = 0x0a, .limit = 4000 },
+ { .rfmax = 230000, .target = 0x0a, .limit = 4000 },
+ { .rfmax = 345000, .target = 0x18, .limit = 4000 },
+ { .rfmax = 426000, .target = 0x0e, .limit = 4000 },
+ { .rfmax = 489500, .target = 0x1e, .limit = 4000 },
+ { .rfmax = 697500, .target = 0x32, .limit = 4000 },
+ { .rfmax = 842000, .target = 0x3a, .limit = 4000 },
+ { .rfmax = 0, .target = 0x00, .limit = 0 }, /* end */
+};
+
+int tda18271_lookup_cid_target(struct dvb_frontend *fe,
+ u32 *freq, u8 *cid_target, u16 *count_limit)
+{
+ int i = 0;
+
+ while ((tda18271_cid_target[i].rfmax * 1000) < *freq) {
+ if (tda18271_cid_target[i + 1].rfmax == 0)
+ break;
+ i++;
+ }
+ *cid_target = tda18271_cid_target[i].target;
+ *count_limit = tda18271_cid_target[i].limit;
+
+ tda_map("(%d) cid_target = %02x, count_limit = %d\n", i,
+ tda18271_cid_target[i].target, tda18271_cid_target[i].limit);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+static struct tda18271_rf_tracking_filter_cal tda18271_rf_band_template[] = {
+ { .rfmax = 47900, .rfband = 0x00,
+ .rf1_def = 46000, .rf2_def = 0, .rf3_def = 0 },
+ { .rfmax = 61100, .rfband = 0x01,
+ .rf1_def = 52200, .rf2_def = 0, .rf3_def = 0 },
+ { .rfmax = 152600, .rfband = 0x02,
+ .rf1_def = 70100, .rf2_def = 136800, .rf3_def = 0 },
+ { .rfmax = 164700, .rfband = 0x03,
+ .rf1_def = 156700, .rf2_def = 0, .rf3_def = 0 },
+ { .rfmax = 203500, .rfband = 0x04,
+ .rf1_def = 186250, .rf2_def = 0, .rf3_def = 0 },
+ { .rfmax = 457800, .rfband = 0x05,
+ .rf1_def = 230000, .rf2_def = 345000, .rf3_def = 426000 },
+ { .rfmax = 865000, .rfband = 0x06,
+ .rf1_def = 489500, .rf2_def = 697500, .rf3_def = 842000 },
+ { .rfmax = 0, .rfband = 0x00,
+ .rf1_def = 0, .rf2_def = 0, .rf3_def = 0 }, /* end */
+};
+
+int tda18271_lookup_rf_band(struct dvb_frontend *fe, u32 *freq, u8 *rf_band)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
+ int i = 0;
+
+ while ((map[i].rfmax * 1000) < *freq) {
+ if (tda18271_debug & DBG_ADV)
+ tda_map("(%d) rfmax = %d < freq = %d, "
+ "rf1_def = %d, rf2_def = %d, rf3_def = %d, "
+ "rf1 = %d, rf2 = %d, rf3 = %d, "
+ "rf_a1 = %d, rf_a2 = %d, "
+ "rf_b1 = %d, rf_b2 = %d\n",
+ i, map[i].rfmax * 1000, *freq,
+ map[i].rf1_def, map[i].rf2_def, map[i].rf3_def,
+ map[i].rf1, map[i].rf2, map[i].rf3,
+ map[i].rf_a1, map[i].rf_a2,
+ map[i].rf_b1, map[i].rf_b2);
+ if (map[i].rfmax == 0)
+ return -EINVAL;
+ i++;
+ }
+ if (rf_band)
+ *rf_band = map[i].rfband;
+
+ tda_map("(%d) rf_band = %02x\n", i, map[i].rfband);
+
+ return i;
+}
+
+/*---------------------------------------------------------------------*/
+
+struct tda18271_map_layout {
+ struct tda18271_pll_map *main_pll;
+ struct tda18271_pll_map *cal_pll;
+
+ struct tda18271_map *rf_cal;
+ struct tda18271_map *rf_cal_kmco;
+ struct tda18271_map *rf_cal_dc_over_dt;
+
+ struct tda18271_map *bp_filter;
+ struct tda18271_map *rf_band;
+ struct tda18271_map *gain_taper;
+ struct tda18271_map *ir_measure;
+};
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_lookup_pll_map(struct dvb_frontend *fe,
+ enum tda18271_map_type map_type,
+ u32 *freq, u8 *post_div, u8 *div)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_pll_map *map = NULL;
+ unsigned int i = 0;
+ char *map_name;
+ int ret = 0;
+
+ BUG_ON(!priv->maps);
+
+ switch (map_type) {
+ case MAIN_PLL:
+ map = priv->maps->main_pll;
+ map_name = "main_pll";
+ break;
+ case CAL_PLL:
+ map = priv->maps->cal_pll;
+ map_name = "cal_pll";
+ break;
+ default:
+ /* we should never get here */
+ map_name = "undefined";
+ break;
+ }
+
+ if (!map) {
+ tda_warn("%s map is not set!\n", map_name);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ while ((map[i].lomax * 1000) < *freq) {
+ if (map[i + 1].lomax == 0) {
+ tda_map("%s: frequency (%d) out of range\n",
+ map_name, *freq);
+ ret = -ERANGE;
+ break;
+ }
+ i++;
+ }
+ *post_div = map[i].pd;
+ *div = map[i].d;
+
+ tda_map("(%d) %s: post div = 0x%02x, div = 0x%02x\n",
+ i, map_name, *post_div, *div);
+fail:
+ return ret;
+}
+
+int tda18271_lookup_map(struct dvb_frontend *fe,
+ enum tda18271_map_type map_type,
+ u32 *freq, u8 *val)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_map *map = NULL;
+ unsigned int i = 0;
+ char *map_name;
+ int ret = 0;
+
+ BUG_ON(!priv->maps);
+
+ switch (map_type) {
+ case BP_FILTER:
+ map = priv->maps->bp_filter;
+ map_name = "bp_filter";
+ break;
+ case RF_CAL_KMCO:
+ map = priv->maps->rf_cal_kmco;
+ map_name = "km";
+ break;
+ case RF_BAND:
+ map = priv->maps->rf_band;
+ map_name = "rf_band";
+ break;
+ case GAIN_TAPER:
+ map = priv->maps->gain_taper;
+ map_name = "gain_taper";
+ break;
+ case RF_CAL:
+ map = priv->maps->rf_cal;
+ map_name = "rf_cal";
+ break;
+ case IR_MEASURE:
+ map = priv->maps->ir_measure;
+ map_name = "ir_measure";
+ break;
+ case RF_CAL_DC_OVER_DT:
+ map = priv->maps->rf_cal_dc_over_dt;
+ map_name = "rf_cal_dc_over_dt";
+ break;
+ default:
+ /* we should never get here */
+ map_name = "undefined";
+ break;
+ }
+
+ if (!map) {
+ tda_warn("%s map is not set!\n", map_name);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ while ((map[i].rfmax * 1000) < *freq) {
+ if (map[i + 1].rfmax == 0) {
+ tda_map("%s: frequency (%d) out of range\n",
+ map_name, *freq);
+ ret = -ERANGE;
+ break;
+ }
+ i++;
+ }
+ *val = map[i].val;
+
+ tda_map("(%d) %s: 0x%02x\n", i, map_name, *val);
+fail:
+ return ret;
+}
+
+/*---------------------------------------------------------------------*/
+
+static struct tda18271_std_map tda18271c1_std_map = {
+ .fm_radio = { .if_freq = 1250, .std_bits = 0x18 },
+ .atv_b = { .if_freq = 6750, .std_bits = 0x0e },
+ .atv_dk = { .if_freq = 7750, .std_bits = 0x0f },
+ .atv_gh = { .if_freq = 7750, .std_bits = 0x0f },
+ .atv_i = { .if_freq = 7750, .std_bits = 0x0f },
+ .atv_l = { .if_freq = 7750, .std_bits = 0x0f },
+ .atv_lc = { .if_freq = 1250, .std_bits = 0x0f },
+ .atv_mn = { .if_freq = 5750, .std_bits = 0x0d },
+ .atsc_6 = { .if_freq = 3250, .std_bits = 0x1c },
+ .dvbt_6 = { .if_freq = 3300, .std_bits = 0x1c },
+ .dvbt_7 = { .if_freq = 3800, .std_bits = 0x1d },
+ .dvbt_8 = { .if_freq = 4300, .std_bits = 0x1e },
+ .qam_6 = { .if_freq = 4000, .std_bits = 0x1d },
+ .qam_8 = { .if_freq = 5000, .std_bits = 0x1f },
+};
+
+static struct tda18271_std_map tda18271c2_std_map = {
+ .fm_radio = { .if_freq = 1250, .std_bits = 0x18 },
+ .atv_b = { .if_freq = 6000, .std_bits = 0x0d },
+ .atv_dk = { .if_freq = 6900, .std_bits = 0x0e },
+ .atv_gh = { .if_freq = 7100, .std_bits = 0x0e },
+ .atv_i = { .if_freq = 7250, .std_bits = 0x0e },
+ .atv_l = { .if_freq = 6900, .std_bits = 0x0e },
+ .atv_lc = { .if_freq = 1250, .std_bits = 0x0e },
+ .atv_mn = { .if_freq = 5400, .std_bits = 0x0c },
+ .atsc_6 = { .if_freq = 3250, .std_bits = 0x1c },
+ .dvbt_6 = { .if_freq = 3300, .std_bits = 0x1c },
+ .dvbt_7 = { .if_freq = 3500, .std_bits = 0x1c },
+ .dvbt_8 = { .if_freq = 4000, .std_bits = 0x1d },
+ .qam_6 = { .if_freq = 4000, .std_bits = 0x1d },
+ .qam_8 = { .if_freq = 5000, .std_bits = 0x1f },
+};
+
+/*---------------------------------------------------------------------*/
+
+static struct tda18271_map_layout tda18271c1_map_layout = {
+ .main_pll = tda18271c1_main_pll,
+ .cal_pll = tda18271c1_cal_pll,
+
+ .rf_cal = tda18271c1_rf_cal,
+ .rf_cal_kmco = tda18271c1_km,
+
+ .bp_filter = tda18271_bp_filter,
+ .rf_band = tda18271_rf_band,
+ .gain_taper = tda18271_gain_taper,
+ .ir_measure = tda18271_ir_measure,
+};
+
+static struct tda18271_map_layout tda18271c2_map_layout = {
+ .main_pll = tda18271c2_main_pll,
+ .cal_pll = tda18271c2_cal_pll,
+
+ .rf_cal = tda18271c2_rf_cal,
+ .rf_cal_kmco = tda18271c2_km,
+
+ .rf_cal_dc_over_dt = tda18271_rf_cal_dc_over_dt,
+
+ .bp_filter = tda18271_bp_filter,
+ .rf_band = tda18271_rf_band,
+ .gain_taper = tda18271_gain_taper,
+ .ir_measure = tda18271_ir_measure,
+};
+
+int tda18271_assign_map_layout(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ int ret = 0;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ priv->maps = &tda18271c1_map_layout;
+ memcpy(&priv->std, &tda18271c1_std_map,
+ sizeof(struct tda18271_std_map));
+ break;
+ case TDA18271HDC2:
+ priv->maps = &tda18271c2_map_layout;
+ memcpy(&priv->std, &tda18271c2_std_map,
+ sizeof(struct tda18271_std_map));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ memcpy(priv->rf_cal_state, &tda18271_rf_band_template,
+ sizeof(tda18271_rf_band_template));
+
+ return ret;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/trunk/drivers/media/dvb/frontends/tda18271.h b/trunk/drivers/media/dvb/frontends/tda18271.h
new file mode 100644
index 000000000000..24b0e35a2ab3
--- /dev/null
+++ b/trunk/drivers/media/dvb/frontends/tda18271.h
@@ -0,0 +1,78 @@
+/*
+ tda18271.h - header for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __TDA18271_H__
+#define __TDA18271_H__
+
+#include
+#include "dvb_frontend.h"
+
+struct tda18271_std_map_item {
+ u16 if_freq;
+ u8 std_bits;
+};
+
+struct tda18271_std_map {
+ struct tda18271_std_map_item fm_radio;
+ struct tda18271_std_map_item atv_b;
+ struct tda18271_std_map_item atv_dk;
+ struct tda18271_std_map_item atv_gh;
+ struct tda18271_std_map_item atv_i;
+ struct tda18271_std_map_item atv_l;
+ struct tda18271_std_map_item atv_lc;
+ struct tda18271_std_map_item atv_mn;
+ struct tda18271_std_map_item atsc_6;
+ struct tda18271_std_map_item dvbt_6;
+ struct tda18271_std_map_item dvbt_7;
+ struct tda18271_std_map_item dvbt_8;
+ struct tda18271_std_map_item qam_6;
+ struct tda18271_std_map_item qam_8;
+};
+
+enum tda18271_i2c_gate {
+ TDA18271_GATE_AUTO = 0,
+ TDA18271_GATE_ANALOG,
+ TDA18271_GATE_DIGITAL,
+};
+
+struct tda18271_config {
+ /* override default if freq / std settings (optional) */
+ struct tda18271_std_map *std_map;
+
+ /* use i2c gate provided by analog or digital demod */
+ enum tda18271_i2c_gate gate;
+};
+
+#if defined(CONFIG_DVB_TDA18271) || (defined(CONFIG_DVB_TDA18271_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg);
+#else
+static inline struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe,
+ u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif
+
+#endif /* __TDA18271_H__ */
diff --git a/trunk/drivers/media/dvb/frontends/tda827x.c b/trunk/drivers/media/dvb/frontends/tda827x.c
index 256fc4bf500b..229b11987a58 100644
--- a/trunk/drivers/media/dvb/frontends/tda827x.c
+++ b/trunk/drivers/media/dvb/frontends/tda827x.c
@@ -19,12 +19,16 @@
*/
#include
-#include
#include
+#include
+#include
#include "tda827x.h"
static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
#define dprintk(args...) \
do { \
if (debug) printk(KERN_DEBUG "tda827x: " args); \
@@ -34,10 +38,57 @@ struct tda827x_priv {
int i2c_addr;
struct i2c_adapter *i2c_adap;
struct tda827x_config *cfg;
+
+ unsigned int sgIF;
+ unsigned char lpsel;
+
u32 frequency;
u32 bandwidth;
};
+static void tda827x_set_std(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ char *mode;
+
+ priv->lpsel = 0;
+ if (params->std & V4L2_STD_MN) {
+ priv->sgIF = 92;
+ priv->lpsel = 1;
+ mode = "MN";
+ } else if (params->std & V4L2_STD_B) {
+ priv->sgIF = 108;
+ mode = "B";
+ } else if (params->std & V4L2_STD_GH) {
+ priv->sgIF = 124;
+ mode = "GH";
+ } else if (params->std & V4L2_STD_PAL_I) {
+ priv->sgIF = 124;
+ mode = "I";
+ } else if (params->std & V4L2_STD_DK) {
+ priv->sgIF = 124;
+ mode = "DK";
+ } else if (params->std & V4L2_STD_SECAM_L) {
+ priv->sgIF = 124;
+ mode = "L";
+ } else if (params->std & V4L2_STD_SECAM_LC) {
+ priv->sgIF = 20;
+ mode = "LC";
+ } else {
+ priv->sgIF = 124;
+ mode = "xx";
+ }
+
+ if (params->mode == V4L2_TUNER_RADIO)
+ priv->sgIF = 88; /* if frequency is 5.5 MHz */
+
+ dprintk("setting tda827x to system %s\n", mode);
+}
+
+
+/* ------------------------------------------------------------------ */
+
struct tda827x_data {
u32 lomax;
u8 spd;
@@ -48,7 +99,7 @@ struct tda827x_data {
u8 div1p5;
};
-static const struct tda827x_data tda827x_dvbt[] = {
+static const struct tda827x_data tda827x_table[] = {
{ .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
{ .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
{ .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
@@ -106,21 +157,22 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
tuner_freq = params->frequency + if_freq;
i = 0;
- while (tda827x_dvbt[i].lomax < tuner_freq) {
- if(tda827x_dvbt[i + 1].lomax == 0)
+ while (tda827x_table[i].lomax < tuner_freq) {
+ if (tda827x_table[i + 1].lomax == 0)
break;
i++;
}
- N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2);
+ N = ((tuner_freq + 125000) / 250000) << (tda827x_table[i].spd + 2);
buf[0] = 0;
buf[1] = (N>>8) | 0x40;
buf[2] = N & 0xff;
buf[3] = 0;
buf[4] = 0x52;
- buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) +
- (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp;
- buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f;
+ buf[5] = (tda827x_table[i].spd << 6) + (tda827x_table[i].div1p5 << 5) +
+ (tda827x_table[i].bs << 3) +
+ tda827x_table[i].bp;
+ buf[6] = (tda827x_table[i].gc3 << 4) + 0x8f;
buf[7] = 0xbf;
buf[8] = 0x2a;
buf[9] = 0x05;
@@ -140,7 +192,7 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
msleep(500);
/* correct CP value */
buf[0] = 0x30;
- buf[1] = 0x50 + tda827x_dvbt[i].cp;
+ buf[1] = 0x50 + tda827x_table[i].cp;
msg.len = 2;
if (fe->ops.i2c_gate_ctrl)
@@ -173,6 +225,102 @@ static int tda827xo_sleep(struct dvb_frontend *fe)
/* ------------------------------------------------------------------ */
+static int tda827xo_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ unsigned char tuner_reg[8];
+ unsigned char reg2[2];
+ u32 N;
+ int i;
+ struct tda827x_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0 };
+ unsigned int freq = params->frequency;
+
+ tda827x_set_std(fe, params);
+
+ if (params->mode == V4L2_TUNER_RADIO)
+ freq = freq / 1000;
+
+ N = freq + priv->sgIF;
+
+ i = 0;
+ while (tda827x_table[i].lomax < N * 62500) {
+ if (tda827x_table[i + 1].lomax == 0)
+ break;
+ i++;
+ }
+
+ N = N << tda827x_table[i].spd;
+
+ tuner_reg[0] = 0;
+ tuner_reg[1] = (unsigned char)(N>>8);
+ tuner_reg[2] = (unsigned char) N;
+ tuner_reg[3] = 0x40;
+ tuner_reg[4] = 0x52 + (priv->lpsel << 5);
+ tuner_reg[5] = (tda827x_table[i].spd << 6) +
+ (tda827x_table[i].div1p5 << 5) +
+ (tda827x_table[i].bs << 3) + tda827x_table[i].bp;
+ tuner_reg[6] = 0x8f + (tda827x_table[i].gc3 << 4);
+ tuner_reg[7] = 0x8f;
+
+ msg.buf = tuner_reg;
+ msg.len = 8;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msg.buf = reg2;
+ msg.len = 2;
+ reg2[0] = 0x80;
+ reg2[1] = 0;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ reg2[0] = 0x60;
+ reg2[1] = 0xbf;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4] + 0x80;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(1);
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4] + 4;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(1);
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4];
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(550);
+ reg2[0] = 0x30;
+ reg2[1] = (tuner_reg[4] & 0xfc) + tda827x_table[i].cp;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ reg2[0] = 0x60;
+ reg2[1] = 0x3f;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ reg2[0] = 0x80;
+ reg2[1] = 0x08; /* Vsync en */
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ priv->frequency = freq * 62500;
+
+ return 0;
+}
+
+static void tda827xo_agcf(struct dvb_frontend *fe)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ unsigned char data[] = { 0x80, 0x0c };
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = data, .len = 2};
+
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+}
+
+/* ------------------------------------------------------------------ */
+
struct tda827xa_data {
u32 lomax;
u8 svco;
@@ -212,6 +360,35 @@ static const struct tda827xa_data tda827xa_dvbt[] = {
{ .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
};
+static struct tda827xa_data tda827xa_analog[] = {
+ { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 3},
+ { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
+ { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
+ { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
+ { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
+ { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 3},
+ { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 3},
+ { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
+ { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3},
+ { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3},
+ { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
+ { .lomax = 554000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0},
+ { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
+};
+
static int tda827xa_set_params(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params)
{
@@ -368,6 +545,163 @@ static int tda827xa_sleep(struct dvb_frontend *fe)
return 0;
}
+/* ------------------------------------------------------------------ */
+
+static void tda827xa_lna_gain(struct dvb_frontend *fe, int high,
+ struct analog_parameters *params)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ unsigned char buf[] = {0x22, 0x01};
+ int arg;
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+
+ if (NULL == priv->cfg) {
+ dprintk("tda827x_config not defined, cannot set LNA gain!\n");
+ return;
+ }
+
+ if (priv->cfg->config) {
+ if (high)
+ dprintk("setting LNA to high gain\n");
+ else
+ dprintk("setting LNA to low gain\n");
+ }
+ switch (*priv->cfg->config) {
+ case 0: /* no LNA */
+ break;
+ case 1: /* switch is GPIO 0 of tda8290 */
+ case 2:
+ /* turn Vsync on */
+ if (params->std & V4L2_STD_MN)
+ arg = 1;
+ else
+ arg = 0;
+ if (priv->cfg->tuner_callback)
+ priv->cfg->tuner_callback(priv->i2c_adap->algo_data,
+ 1, arg);
+ buf[1] = high ? 0 : 1;
+ if (*priv->cfg->config == 2)
+ buf[1] = high ? 1 : 0;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+ break;
+ case 3: /* switch with GPIO of saa713x */
+ if (priv->cfg->tuner_callback)
+ priv->cfg->tuner_callback(priv->i2c_adap->algo_data,
+ 0, high);
+ break;
+ }
+}
+
+static int tda827xa_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ unsigned char tuner_reg[11];
+ u32 N;
+ int i;
+ struct tda827x_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = tuner_reg, .len = sizeof(tuner_reg) };
+ unsigned int freq = params->frequency;
+
+ tda827x_set_std(fe, params);
+
+ tda827xa_lna_gain(fe, 1, params);
+ msleep(10);
+
+ if (params->mode == V4L2_TUNER_RADIO)
+ freq = freq / 1000;
+
+ N = freq + priv->sgIF;
+
+ i = 0;
+ while (tda827xa_analog[i].lomax < N * 62500) {
+ if (tda827xa_analog[i + 1].lomax == 0)
+ break;
+ i++;
+ }
+
+ N = N << tda827xa_analog[i].spd;
+
+ tuner_reg[0] = 0;
+ tuner_reg[1] = (unsigned char)(N>>8);
+ tuner_reg[2] = (unsigned char) N;
+ tuner_reg[3] = 0;
+ tuner_reg[4] = 0x16;
+ tuner_reg[5] = (tda827xa_analog[i].spd << 5) +
+ (tda827xa_analog[i].svco << 3) +
+ tda827xa_analog[i].sbs;
+ tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4);
+ tuner_reg[7] = 0x1c;
+ tuner_reg[8] = 4;
+ tuner_reg[9] = 0x20;
+ tuner_reg[10] = 0x00;
+ msg.len = 11;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0x90;
+ tuner_reg[1] = 0xff;
+ tuner_reg[2] = 0xe0;
+ tuner_reg[3] = 0;
+ tuner_reg[4] = 0x99 + (priv->lpsel << 1);
+ msg.len = 5;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0xa0;
+ tuner_reg[1] = 0xc0;
+ msg.len = 2;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0x30;
+ tuner_reg[1] = 0x10 + tda827xa_analog[i].scr;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msg.flags = I2C_M_RD;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+ msg.flags = 0;
+ tuner_reg[1] >>= 4;
+ dprintk("AGC2 gain is: %d\n", tuner_reg[1]);
+ if (tuner_reg[1] < 1)
+ tda827xa_lna_gain(fe, 0, params);
+
+ msleep(100);
+ tuner_reg[0] = 0x60;
+ tuner_reg[1] = 0x3c;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(163);
+ tuner_reg[0] = 0x50;
+ tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0x80;
+ tuner_reg[1] = 0x28;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0xb0;
+ tuner_reg[1] = 0x01;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0xc0;
+ tuner_reg[1] = 0x19 + (priv->lpsel << 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ priv->frequency = freq * 62500;
+
+ return 0;
+}
+
+static void tda827xa_agcf(struct dvb_frontend *fe)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ unsigned char data[] = {0x80, 0x2c};
+ struct i2c_msg msg = {.addr = priv->i2c_addr, .flags = 0,
+ .buf = data, .len = 2};
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+}
+
+/* ------------------------------------------------------------------ */
+
static int tda827x_release(struct dvb_frontend *fe)
{
kfree(fe->tuner_priv);
@@ -430,6 +764,7 @@ static struct dvb_tuner_ops tda827xo_tuner_ops = {
.init = tda827x_initial_init,
.sleep = tda827x_initial_sleep,
.set_params = tda827xo_set_params,
+ .set_analog_params = tda827xo_set_analog_params,
.get_frequency = tda827x_get_frequency,
.get_bandwidth = tda827x_get_bandwidth,
};
@@ -445,6 +780,7 @@ static struct dvb_tuner_ops tda827xa_tuner_ops = {
.init = tda827x_init,
.sleep = tda827xa_sleep,
.set_params = tda827xa_set_params,
+ .set_analog_params = tda827xa_set_analog_params,
.get_frequency = tda827x_get_frequency,
.get_bandwidth = tda827x_get_bandwidth,
};
@@ -465,9 +801,13 @@ static int tda827x_probe_version(struct dvb_frontend *fe)
dprintk("tda827x tuner found\n");
fe->ops.tuner_ops.init = tda827x_init;
fe->ops.tuner_ops.sleep = tda827xo_sleep;
+ if (priv->cfg)
+ priv->cfg->agcf = tda827xo_agcf;
} else {
dprintk("tda827xa tuner found\n");
memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops));
+ if (priv->cfg)
+ priv->cfg->agcf = tda827xa_agcf;
}
return 0;
}
@@ -487,16 +827,13 @@ struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr,
priv->i2c_adap = i2c;
priv->cfg = cfg;
memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops));
-
fe->tuner_priv = priv;
+ dprintk("type set to %s\n", fe->ops.tuner_ops.info.name);
+
return fe;
}
-
-EXPORT_SYMBOL(tda827x_attach);
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+EXPORT_SYMBOL_GPL(tda827x_attach);
MODULE_DESCRIPTION("DVB TDA827x driver");
MODULE_AUTHOR("Hartmut Hackmann ");
diff --git a/trunk/drivers/media/dvb/frontends/tda827x.h b/trunk/drivers/media/dvb/frontends/tda827x.h
index 69e8263d6d59..92eb65b4012b 100644
--- a/trunk/drivers/media/dvb/frontends/tda827x.h
+++ b/trunk/drivers/media/dvb/frontends/tda827x.h
@@ -29,9 +29,16 @@
struct tda827x_config
{
+ /* saa7134 - provided callbacks */
void (*lna_gain) (struct dvb_frontend *fe, int high);
int (*init) (struct dvb_frontend *fe);
int (*sleep) (struct dvb_frontend *fe);
+
+ /* interface to tda829x driver */
+ unsigned int *config;
+ int (*tuner_callback) (void *dev, int command, int arg);
+
+ void (*agcf)(struct dvb_frontend *fe);
};
diff --git a/trunk/drivers/media/dvb/frontends/ves1820.c b/trunk/drivers/media/dvb/frontends/ves1820.c
index 60433b5011fd..8791701c8f25 100644
--- a/trunk/drivers/media/dvb/frontends/ves1820.c
+++ b/trunk/drivers/media/dvb/frontends/ves1820.c
@@ -65,7 +65,7 @@ static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data)
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
- printk("ves1820: %s(): writereg error (reg == 0x%02x,"
+ printk("ves1820: %s(): writereg error (reg == 0x%02x, "
"val == 0x%02x, ret == %i)\n", __FUNCTION__, reg, data, ret);
return (ret != 1) ? -EREMOTEIO : 0;
@@ -84,7 +84,7 @@ static u8 ves1820_readreg(struct ves1820_state *state, u8 reg)
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2)
- printk("ves1820: %s(): readreg error (reg == 0x%02x,"
+ printk("ves1820: %s(): readreg error (reg == 0x%02x, "
"ret == %i)\n", __FUNCTION__, reg, ret);
return b1[0];
diff --git a/trunk/drivers/media/dvb/frontends/xc5000.c b/trunk/drivers/media/dvb/frontends/xc5000.c
new file mode 100644
index 000000000000..f642ca200b59
--- /dev/null
+++ b/trunk/drivers/media/dvb/frontends/xc5000.c
@@ -0,0 +1,964 @@
+/*
+ * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 Xceive Corporation
+ * Copyright (c) 2007 Steven Toth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "dvb_frontend.h"
+
+#include "xc5000.h"
+#include "xc5000_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
+
+#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.1.fw"
+#define XC5000_DEFAULT_FIRMWARE_SIZE 12332
+
+/* Misc Defines */
+#define MAX_TV_STANDARD 23
+#define XC_MAX_I2C_WRITE_LENGTH 64
+
+/* Signal Types */
+#define XC_RF_MODE_AIR 0
+#define XC_RF_MODE_CABLE 1
+
+/* Result codes */
+#define XC_RESULT_SUCCESS 0
+#define XC_RESULT_RESET_FAILURE 1
+#define XC_RESULT_I2C_WRITE_FAILURE 2
+#define XC_RESULT_I2C_READ_FAILURE 3
+#define XC_RESULT_OUT_OF_RANGE 5
+
+/* Product id */
+#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000
+#define XC_PRODUCT_ID_FW_LOADED 0x1388
+
+/* Registers */
+#define XREG_INIT 0x00
+#define XREG_VIDEO_MODE 0x01
+#define XREG_AUDIO_MODE 0x02
+#define XREG_RF_FREQ 0x03
+#define XREG_D_CODE 0x04
+#define XREG_IF_OUT 0x05
+#define XREG_SEEK_MODE 0x07
+#define XREG_POWER_DOWN 0x0A
+#define XREG_SIGNALSOURCE 0x0D /* 0=Air, 1=Cable */
+#define XREG_SMOOTHEDCVBS 0x0E
+#define XREG_XTALFREQ 0x0F
+#define XREG_FINERFFREQ 0x10
+#define XREG_DDIMODE 0x11
+
+#define XREG_ADC_ENV 0x00
+#define XREG_QUALITY 0x01
+#define XREG_FRAME_LINES 0x02
+#define XREG_HSYNC_FREQ 0x03
+#define XREG_LOCK 0x04
+#define XREG_FREQ_ERROR 0x05
+#define XREG_SNR 0x06
+#define XREG_VERSION 0x07
+#define XREG_PRODUCT_ID 0x08
+#define XREG_BUSY 0x09
+
+/*
+ Basic firmware description. This will remain with
+ the driver for documentation purposes.
+
+ This represents an I2C firmware file encoded as a
+ string of unsigned char. Format is as follows:
+
+ char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB
+ char[1 ]=len0_LSB -> length of first write transaction
+ char[2 ]=data0 -> first byte to be sent
+ char[3 ]=data1
+ char[4 ]=data2
+ char[ ]=...
+ char[M ]=dataN -> last byte to be sent
+ char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB
+ char[M+2]=len1_LSB -> length of second write transaction
+ char[M+3]=data0
+ char[M+4]=data1
+ ...
+ etc.
+
+ The [len] value should be interpreted as follows:
+
+ len= len_MSB _ len_LSB
+ len=1111_1111_1111_1111 : End of I2C_SEQUENCE
+ len=0000_0000_0000_0000 : Reset command: Do hardware reset
+ len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767)
+ len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms
+
+ For the RESET and WAIT commands, the two following bytes will contain
+ immediately the length of the following transaction.
+
+*/
+typedef struct {
+ char *Name;
+ u16 AudioMode;
+ u16 VideoMode;
+} XC_TV_STANDARD;
+
+/* Tuner standards */
+#define MN_NTSC_PAL_BTSC 0
+#define MN_NTSC_PAL_A2 1
+#define MN_NTSC_PAL_EIAJ 2
+#define MN_NTSC_PAL_Mono 3
+#define BG_PAL_A2 4
+#define BG_PAL_NICAM 5
+#define BG_PAL_MONO 6
+#define I_PAL_NICAM 7
+#define I_PAL_NICAM_MONO 8
+#define DK_PAL_A2 9
+#define DK_PAL_NICAM 10
+#define DK_PAL_MONO 11
+#define DK_SECAM_A2DK1 12
+#define DK_SECAM_A2LDK3 13
+#define DK_SECAM_A2MONO 14
+#define L_SECAM_NICAM 15
+#define LC_SECAM_NICAM 16
+#define DTV6 17
+#define DTV8 18
+#define DTV7_8 19
+#define DTV7 20
+#define FM_Radio_INPUT2 21
+#define FM_Radio_INPUT1 22
+
+XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
+ {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},
+ {"M/N-NTSC/PAL-A2", 0x0600, 0x8020},
+ {"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020},
+ {"M/N-NTSC/PAL-Mono", 0x0478, 0x8020},
+ {"B/G-PAL-A2", 0x0A00, 0x8049},
+ {"B/G-PAL-NICAM", 0x0C04, 0x8049},
+ {"B/G-PAL-MONO", 0x0878, 0x8059},
+ {"I-PAL-NICAM", 0x1080, 0x8009},
+ {"I-PAL-NICAM-MONO", 0x0E78, 0x8009},
+ {"D/K-PAL-A2", 0x1600, 0x8009},
+ {"D/K-PAL-NICAM", 0x0E80, 0x8009},
+ {"D/K-PAL-MONO", 0x1478, 0x8009},
+ {"D/K-SECAM-A2 DK1", 0x1200, 0x8009},
+ {"D/K-SECAM-A2 L/DK3",0x0E00, 0x8009},
+ {"D/K-SECAM-A2 MONO", 0x1478, 0x8009},
+ {"L-SECAM-NICAM", 0x8E82, 0x0009},
+ {"L'-SECAM-NICAM", 0x8E82, 0x4009},
+ {"DTV6", 0x00C0, 0x8002},
+ {"DTV8", 0x00C0, 0x800B},
+ {"DTV7/8", 0x00C0, 0x801B},
+ {"DTV7", 0x00C0, 0x8007},
+ {"FM Radio-INPUT2", 0x9802, 0x9002},
+ {"FM Radio-INPUT1", 0x0208, 0x9002}
+};
+
+static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len);
+static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len);
+static void xc5000_TunerReset(struct dvb_frontend *fe);
+
+static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
+{
+ return xc5000_writeregs(priv, buf, len)
+ ? XC_RESULT_I2C_WRITE_FAILURE : XC_RESULT_SUCCESS;
+}
+
+static int xc_read_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
+{
+ return xc5000_readregs(priv, buf, len)
+ ? XC_RESULT_I2C_READ_FAILURE : XC_RESULT_SUCCESS;
+}
+
+static int xc_reset(struct dvb_frontend *fe)
+{
+ xc5000_TunerReset(fe);
+ return XC_RESULT_SUCCESS;
+}
+
+static void xc_wait(int wait_ms)
+{
+ msleep(wait_ms);
+}
+
+static void xc5000_TunerReset(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (priv->cfg->tuner_callback) {
+ ret = priv->cfg->tuner_callback(priv->cfg->priv,
+ XC5000_TUNER_RESET, 0);
+ if (ret)
+ printk(KERN_ERR "xc5000: reset failed\n");
+ } else
+ printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n");
+}
+
+static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData)
+{
+ u8 buf[4];
+ int WatchDogTimer = 5;
+ int result;
+
+ buf[0] = (regAddr >> 8) & 0xFF;
+ buf[1] = regAddr & 0xFF;
+ buf[2] = (i2cData >> 8) & 0xFF;
+ buf[3] = i2cData & 0xFF;
+ result = xc_send_i2c_data(priv, buf, 4);
+ if (result == XC_RESULT_SUCCESS) {
+ /* wait for busy flag to clear */
+ while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) {
+ buf[0] = 0;
+ buf[1] = XREG_BUSY;
+
+ result = xc_send_i2c_data(priv, buf, 2);
+ if (result == XC_RESULT_SUCCESS) {
+ result = xc_read_i2c_data(priv, buf, 2);
+ if (result == XC_RESULT_SUCCESS) {
+ if ((buf[0] == 0) && (buf[1] == 0)) {
+ /* busy flag cleared */
+ break;
+ } else {
+ xc_wait(100); /* wait 5 ms */
+ WatchDogTimer--;
+ }
+ }
+ }
+ }
+ }
+ if (WatchDogTimer < 0)
+ result = XC_RESULT_I2C_WRITE_FAILURE;
+
+ return result;
+}
+
+static int xc_read_reg(struct xc5000_priv *priv, u16 regAddr, u16 *i2cData)
+{
+ u8 buf[2];
+ int result;
+
+ buf[0] = (regAddr >> 8) & 0xFF;
+ buf[1] = regAddr & 0xFF;
+ result = xc_send_i2c_data(priv, buf, 2);
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ result = xc_read_i2c_data(priv, buf, 2);
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ *i2cData = buf[0] * 256 + buf[1];
+ return result;
+}
+
+static int xc_load_i2c_sequence(struct dvb_frontend *fe, u8 i2c_sequence[])
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+
+ int i, nbytes_to_send, result;
+ unsigned int len, pos, index;
+ u8 buf[XC_MAX_I2C_WRITE_LENGTH];
+
+ index=0;
+ while ((i2c_sequence[index]!=0xFF) || (i2c_sequence[index+1]!=0xFF)) {
+ len = i2c_sequence[index]* 256 + i2c_sequence[index+1];
+ if (len == 0x0000) {
+ /* RESET command */
+ result = xc_reset(fe);
+ index += 2;
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+ } else if (len & 0x8000) {
+ /* WAIT command */
+ xc_wait(len & 0x7FFF);
+ index += 2;
+ } else {
+ /* Send i2c data whilst ensuring individual transactions
+ * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
+ */
+ index += 2;
+ buf[0] = i2c_sequence[index];
+ buf[1] = i2c_sequence[index + 1];
+ pos = 2;
+ while (pos < len) {
+ if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2) {
+ nbytes_to_send = XC_MAX_I2C_WRITE_LENGTH;
+ } else {
+ nbytes_to_send = (len - pos + 2);
+ }
+ for (i=2; ivideo_standard].Name);
+
+ ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode);
+ if (ret == XC_RESULT_SUCCESS)
+ ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode);
+
+ return ret;
+}
+
+static int xc_shutdown(struct xc5000_priv *priv)
+{
+ return 0;
+ /* Fixme: cannot bring tuner back alive once shutdown
+ * without reloading the driver modules.
+ * return xc_write_reg(priv, XREG_POWER_DOWN, 0);
+ */
+}
+
+static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode)
+{
+ dprintk(1, "%s(%d) Source = %s\n", __FUNCTION__, rf_mode,
+ rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
+
+ if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE))
+ {
+ rf_mode = XC_RF_MODE_CABLE;
+ printk(KERN_ERR
+ "%s(), Invalid mode, defaulting to CABLE",
+ __FUNCTION__);
+ }
+ return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
+}
+
+static const struct dvb_tuner_ops xc5000_tuner_ops;
+
+static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)
+{
+ u16 freq_code;
+
+ dprintk(1, "%s(%u)\n", __FUNCTION__, freq_hz);
+
+ if ((freq_hz > xc5000_tuner_ops.info.frequency_max) ||
+ (freq_hz < xc5000_tuner_ops.info.frequency_min))
+ return XC_RESULT_OUT_OF_RANGE;
+
+ freq_code = (u16)(freq_hz / 15625);
+
+ return xc_write_reg(priv, XREG_RF_FREQ, freq_code);
+}
+
+
+static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz)
+{
+ u32 freq_code = (freq_khz * 1024)/1000;
+ dprintk(1, "%s(freq_khz = %d) freq_code = 0x%x\n",
+ __FUNCTION__, freq_khz, freq_code);
+
+ return xc_write_reg(priv, XREG_IF_OUT, freq_code);
+}
+
+
+static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope)
+{
+ return xc_read_reg(priv, XREG_ADC_ENV, adc_envelope);
+}
+
+static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz)
+{
+ int result;
+ u16 regData;
+ u32 tmp;
+
+ result = xc_read_reg(priv, XREG_FREQ_ERROR, ®Data);
+ if (result)
+ return result;
+
+ tmp = (u32)regData;
+ (*freq_error_hz) = (tmp * 15625) / 1000;
+ return result;
+}
+
+static int xc_get_lock_status(struct xc5000_priv *priv, u16 *lock_status)
+{
+ return xc_read_reg(priv, XREG_LOCK, lock_status);
+}
+
+static int xc_get_version(struct xc5000_priv *priv,
+ u8 *hw_majorversion, u8 *hw_minorversion,
+ u8 *fw_majorversion, u8 *fw_minorversion)
+{
+ u16 data;
+ int result;
+
+ result = xc_read_reg(priv, XREG_VERSION, &data);
+ if (result)
+ return result;
+
+ (*hw_majorversion) = (data >> 12) & 0x0F;
+ (*hw_minorversion) = (data >> 8) & 0x0F;
+ (*fw_majorversion) = (data >> 4) & 0x0F;
+ (*fw_minorversion) = data & 0x0F;
+
+ return 0;
+}
+
+static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz)
+{
+ u16 regData;
+ int result;
+
+ result = xc_read_reg(priv, XREG_HSYNC_FREQ, ®Data);
+ if (result)
+ return result;
+
+ (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
+ return result;
+}
+
+static int xc_get_frame_lines(struct xc5000_priv *priv, u16 *frame_lines)
+{
+ return xc_read_reg(priv, XREG_FRAME_LINES, frame_lines);
+}
+
+static int xc_get_quality(struct xc5000_priv *priv, u16 *quality)
+{
+ return xc_read_reg(priv, XREG_QUALITY, quality);
+}
+
+static u16 WaitForLock(struct xc5000_priv *priv)
+{
+ u16 lockState = 0;
+ int watchDogCount = 40;
+
+ while ((lockState == 0) && (watchDogCount > 0)) {
+ xc_get_lock_status(priv, &lockState);
+ if (lockState != 1) {
+ xc_wait(5);
+ watchDogCount--;
+ }
+ }
+ return lockState;
+}
+
+static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz)
+{
+ int found = 0;
+
+ dprintk(1, "%s(%u)\n", __FUNCTION__, freq_hz);
+
+ if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS)
+ return 0;
+
+ if (WaitForLock(priv) == 1)
+ found = 1;
+
+ return found;
+}
+
+static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
+{
+ u8 buf[2] = { reg >> 8, reg & 0xff };
+ u8 bval[2] = { 0, 0 };
+ struct i2c_msg msg[2] = {
+ { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = &buf[0], .len = 2 },
+ { .addr = priv->cfg->i2c_address,
+ .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ printk(KERN_WARNING "xc5000: I2C read failed\n");
+ return -EREMOTEIO;
+ }
+
+ *val = (bval[0] << 8) | bval[1];
+ return 0;
+}
+
+static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len)
+{
+ struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = buf, .len = len };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n",
+ (int)len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len)
+{
+ struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ .flags = I2C_M_RD, .buf = buf, .len = len };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n",(int)len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int xc5000_fwupload(struct dvb_frontend* fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ const struct firmware *fw;
+ int ret;
+
+ /* request the firmware, this will block and timeout */
+ printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
+ XC5000_DEFAULT_FIRMWARE);
+
+ ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c->dev);
+ if (ret) {
+ printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
+ ret = XC_RESULT_RESET_FAILURE;
+ goto out;
+ } else {
+ printk(KERN_INFO "xc5000: firmware read %Zu bytes.\n",
+ fw->size);
+ ret = XC_RESULT_SUCCESS;
+ }
+
+ if (fw->size != XC5000_DEFAULT_FIRMWARE_SIZE) {
+ printk(KERN_ERR "xc5000: firmware incorrect size\n");
+ ret = XC_RESULT_RESET_FAILURE;
+ } else {
+ printk(KERN_INFO "xc5000: firmware upload\n");
+ ret = xc_load_i2c_sequence(fe, fw->data );
+ }
+
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static void xc_debug_dump(struct xc5000_priv *priv)
+{
+ u16 adc_envelope;
+ u32 freq_error_hz = 0;
+ u16 lock_status;
+ u32 hsync_freq_hz = 0;
+ u16 frame_lines;
+ u16 quality;
+ u8 hw_majorversion = 0, hw_minorversion = 0;
+ u8 fw_majorversion = 0, fw_minorversion = 0;
+
+ /* Wait for stats to stabilize.
+ * Frame Lines needs two frame times after initial lock
+ * before it is valid.
+ */
+ xc_wait(100);
+
+ xc_get_ADC_Envelope(priv, &adc_envelope);
+ dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
+
+ xc_get_frequency_error(priv, &freq_error_hz);
+ dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
+
+ xc_get_lock_status(priv, &lock_status);
+ dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
+ lock_status);
+
+ xc_get_version(priv, &hw_majorversion, &hw_minorversion,
+ &fw_majorversion, &fw_minorversion);
+ dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n",
+ hw_majorversion, hw_minorversion,
+ fw_majorversion, fw_minorversion);
+
+ xc_get_hsync_freq(priv, &hsync_freq_hz);
+ dprintk(1, "*** Horizontal sync frequency = %d Hz\n", hsync_freq_hz);
+
+ xc_get_frame_lines(priv, &frame_lines);
+ dprintk(1, "*** Frame lines = %d\n", frame_lines);
+
+ xc_get_quality(priv, &quality);
+ dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
+}
+
+static int xc5000_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s() frequency=%d (Hz)\n", __FUNCTION__, params->frequency);
+
+ switch(params->u.vsb.modulation) {
+ case VSB_8:
+ case VSB_16:
+ dprintk(1, "%s() VSB modulation\n", __FUNCTION__);
+ priv->rf_mode = XC_RF_MODE_AIR;
+ priv->freq_hz = params->frequency - 1750000;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = DTV6;
+ break;
+ case QAM_64:
+ case QAM_256:
+ case QAM_AUTO:
+ dprintk(1, "%s() QAM modulation\n", __FUNCTION__);
+ priv->rf_mode = XC_RF_MODE_CABLE;
+ priv->freq_hz = params->frequency - 1750000;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = DTV6;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dprintk(1, "%s() frequency=%d (compensated)\n",
+ __FUNCTION__, priv->freq_hz);
+
+ ret = xc_SetSignalSource(priv, priv->rf_mode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: xc_SetSignalSource(%d) failed\n",
+ priv->rf_mode);
+ return -EREMOTEIO;
+ }
+
+ ret = xc_SetTVStandard(priv,
+ XC5000_Standard[priv->video_standard].VideoMode,
+ XC5000_Standard[priv->video_standard].AudioMode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+ return -EREMOTEIO;
+ }
+
+ ret = xc_set_IF_frequency(priv, priv->cfg->if_khz);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
+ priv->cfg->if_khz);
+ return -EIO;
+ }
+
+ xc_tune_channel(priv, priv->freq_hz);
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return 0;
+}
+
+static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
+
+static int xc5000_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ if(priv->fwloaded == 0)
+ xc_load_fw_and_init_tuner(fe);
+
+ dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
+ __FUNCTION__, params->frequency);
+
+ priv->rf_mode = XC_RF_MODE_CABLE; /* Fix me: it could be air. */
+
+ /* params->frequency is in units of 62.5khz */
+ priv->freq_hz = params->frequency * 62500;
+
+ /* FIX ME: Some video standards may have several possible audio
+ standards. We simply default to one of them here.
+ */
+ if(params->std & V4L2_STD_MN) {
+ /* default to BTSC audio standard */
+ priv->video_standard = MN_NTSC_PAL_BTSC;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_PAL_BG) {
+ /* default to NICAM audio standard */
+ priv->video_standard = BG_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_PAL_I) {
+ /* default to NICAM audio standard */
+ priv->video_standard = I_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_PAL_DK) {
+ /* default to NICAM audio standard */
+ priv->video_standard = DK_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_SECAM_DK) {
+ /* default to A2 DK1 audio standard */
+ priv->video_standard = DK_SECAM_A2DK1;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_SECAM_L) {
+ priv->video_standard = L_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_SECAM_LC) {
+ priv->video_standard = LC_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+tune_channel:
+ ret = xc_SetSignalSource(priv, priv->rf_mode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: xc_SetSignalSource(%d) failed\n",
+ priv->rf_mode);
+ return -EREMOTEIO;
+ }
+
+ ret = xc_SetTVStandard(priv,
+ XC5000_Standard[priv->video_standard].VideoMode,
+ XC5000_Standard[priv->video_standard].AudioMode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+ return -EREMOTEIO;
+ }
+
+ xc_tune_channel(priv, priv->freq_hz);
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return 0;
+}
+
+static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __FUNCTION__);
+ *freq = priv->freq_hz;
+ return 0;
+}
+
+static int xc5000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ *bw = priv->bandwidth;
+ return 0;
+}
+
+static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ u16 lock_status = 0;
+
+ xc_get_lock_status(priv, &lock_status);
+
+ dprintk(1, "%s() lock_status = 0x%08x\n", __FUNCTION__, lock_status);
+
+ *status = lock_status;
+
+ return 0;
+}
+
+static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret = 0;
+
+ if (priv->fwloaded == 0) {
+ ret = xc5000_fwupload(fe);
+ if (ret != XC_RESULT_SUCCESS)
+ return ret;
+ priv->fwloaded = 1;
+ }
+
+ /* Start the tuner self-calibration process */
+ ret |= xc_initialize(priv);
+
+ /* Wait for calibration to complete.
+ * We could continue but XC5000 will clock stretch subsequent
+ * I2C transactions until calibration is complete. This way we
+ * don't have to rely on clock stretching working.
+ */
+ xc_wait( 100 );
+
+ /* Default to "CABLE" mode */
+ ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE);
+
+ return ret;
+}
+
+static int xc5000_sleep(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ /* On Pinnacle PCTV HD 800i, the tuner cannot be reinitialized
+ * once shutdown without reloading the driver. Maybe I am not
+ * doing something right.
+ *
+ */
+
+ ret = xc_shutdown(priv);
+ if(ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: %s() unable to shutdown tuner\n",
+ __FUNCTION__);
+ return -EREMOTEIO;
+ }
+ else {
+ /* priv->fwloaded = 0; */
+ return XC_RESULT_SUCCESS;
+ }
+}
+
+static int xc5000_init(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
+ return -EREMOTEIO;
+ }
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return 0;
+}
+
+static int xc5000_release(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops xc5000_tuner_ops = {
+ .info = {
+ .name = "Xceive XC5000",
+ .frequency_min = 1000000,
+ .frequency_max = 1023000000,
+ .frequency_step = 50000,
+ },
+
+ .release = xc5000_release,
+ .init = xc5000_init,
+ .sleep = xc5000_sleep,
+
+ .set_params = xc5000_set_params,
+ .set_analog_params = xc5000_set_analog_params,
+ .get_frequency = xc5000_get_frequency,
+ .get_bandwidth = xc5000_get_bandwidth,
+ .get_status = xc5000_get_status
+};
+
+struct dvb_frontend * xc5000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc5000_config *cfg)
+{
+ struct xc5000_priv *priv = NULL;
+ u16 id = 0;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ priv = kzalloc(sizeof(struct xc5000_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->i2c = i2c;
+
+ /* Check if firmware has been loaded. It is possible that another
+ instance of the driver has loaded the firmware.
+ */
+ if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) {
+ kfree(priv);
+ return NULL;
+ }
+
+ switch(id) {
+ case XC_PRODUCT_ID_FW_LOADED:
+ printk(KERN_INFO
+ "xc5000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc5000: Firmware has been loaded previously\n");
+ priv->fwloaded = 1;
+ break;
+ case XC_PRODUCT_ID_FW_NOT_LOADED:
+ printk(KERN_INFO
+ "xc5000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc5000: Firmware has not been loaded previously\n");
+ priv->fwloaded = 0;
+ break;
+ default:
+ printk(KERN_ERR
+ "xc5000: Device not found at addr 0x%02x (0x%x)\n",
+ cfg->i2c_address, id);
+ kfree(priv);
+ return NULL;
+ }
+
+ memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ return fe;
+}
+EXPORT_SYMBOL(xc5000_attach);
+
+MODULE_AUTHOR("Steven Toth");
+MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/trunk/drivers/media/dvb/frontends/xc5000.h b/trunk/drivers/media/dvb/frontends/xc5000.h
new file mode 100644
index 000000000000..e0e84562aed1
--- /dev/null
+++ b/trunk/drivers/media/dvb/frontends/xc5000.h
@@ -0,0 +1,62 @@
+/*
+ * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 Steven Toth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __XC5000_H__
+#define __XC5000_H__
+
+#include
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct xc5000_config {
+ u8 i2c_address;
+ u32 if_khz;
+
+ /* For each bridge framework, when it attaches either analog or digital,
+ * it has to store a reference back to its _core equivalent structure,
+ * so that it can service the hardware by steering gpio's etc.
+ * Each bridge implementation is different so cast priv accordingly.
+ * The xc5000 driver cares not for this value, other than ensuring
+ * it's passed back to a bridge during tuner_callback().
+ */
+ void *priv;
+ int (*tuner_callback) (void *priv, int command, int arg);
+};
+
+/* xc5000 callback command */
+#define XC5000_TUNER_RESET 0
+
+#if defined(CONFIG_DVB_TUNER_XC5000) || defined(CONFIG_DVB_TUNER_XC5000_MODULE)
+extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc5000_config *cfg);
+#else
+static inline struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc5000_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TUNER_XC5000
+
+#endif // __XC5000_H__
diff --git a/trunk/drivers/media/dvb/frontends/xc5000_priv.h b/trunk/drivers/media/dvb/frontends/xc5000_priv.h
new file mode 100644
index 000000000000..13b2d19341da
--- /dev/null
+++ b/trunk/drivers/media/dvb/frontends/xc5000_priv.h
@@ -0,0 +1,36 @@
+/*
+ * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 Steven Toth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef XC5000_PRIV_H
+#define XC5000_PRIV_H
+
+struct xc5000_priv {
+ struct xc5000_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 freq_hz;
+ u32 bandwidth;
+ u8 video_standard;
+ u8 rf_mode;
+ u8 fwloaded;
+};
+
+#endif
diff --git a/trunk/drivers/media/dvb/frontends/zl10353.c b/trunk/drivers/media/dvb/frontends/zl10353.c
index 0106df4c55e8..276e3b631dc2 100644
--- a/trunk/drivers/media/dvb/frontends/zl10353.c
+++ b/trunk/drivers/media/dvb/frontends/zl10353.c
@@ -1,7 +1,7 @@
/*
* Driver for Zarlink DVB-T ZL10353 demodulator
*
- * Copyright (C) 2006 Christopher Pascoe
+ * Copyright (C) 2006, 2007 Christopher Pascoe
*
* 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include "dvb_frontend.h"
#include "zl10353_priv.h"
@@ -35,6 +36,8 @@ struct zl10353_state {
struct dvb_frontend frontend;
struct zl10353_config config;
+
+ enum fe_bandwidth bandwidth;
};
static int debug;
@@ -122,9 +125,10 @@ static void zl10353_calc_nominal_rate(struct dvb_frontend *fe,
enum fe_bandwidth bandwidth,
u16 *nominal_rate)
{
- u32 adc_clock = 45056; /* 45.056 MHz */
- u8 bw;
struct zl10353_state *state = fe->demodulator_priv;
+ u32 adc_clock = 450560; /* 45.056 MHz */
+ u64 value;
+ u8 bw;
if (state->config.adc_clock)
adc_clock = state->config.adc_clock;
@@ -142,12 +146,44 @@ static void zl10353_calc_nominal_rate(struct dvb_frontend *fe,
break;
}
- *nominal_rate = (bw * (1 << 23) / 7 * 125 + adc_clock / 2) / adc_clock;
+ value = (u64)10 * (1 << 23) / 7 * 125;
+ value = (bw * value) + adc_clock / 2;
+ do_div(value, adc_clock);
+ *nominal_rate = value;
dprintk("%s: bw %d, adc_clock %d => 0x%x\n",
__FUNCTION__, bw, adc_clock, *nominal_rate);
}
+static void zl10353_calc_input_freq(struct dvb_frontend *fe,
+ u16 *input_freq)
+{
+ struct zl10353_state *state = fe->demodulator_priv;
+ u32 adc_clock = 450560; /* 45.056 MHz */
+ int if2 = 361667; /* 36.1667 MHz */
+ int ife;
+ u64 value;
+
+ if (state->config.adc_clock)
+ adc_clock = state->config.adc_clock;
+ if (state->config.if2)
+ if2 = state->config.if2;
+
+ if (adc_clock >= if2 * 2)
+ ife = if2;
+ else {
+ ife = adc_clock - (if2 % adc_clock);
+ if (ife > adc_clock / 2)
+ ife = adc_clock - ife;
+ }
+ value = (u64)65536 * ife + adc_clock / 2;
+ do_div(value, adc_clock);
+ *input_freq = -value;
+
+ dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n",
+ __FUNCTION__, if2, ife, adc_clock, -(int)value, *input_freq);
+}
+
static int zl10353_sleep(struct dvb_frontend *fe)
{
static u8 zl10353_softdown[] = { 0x50, 0x0C, 0x44 };
@@ -160,64 +196,276 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
struct dvb_frontend_parameters *param)
{
struct zl10353_state *state = fe->demodulator_priv;
- u16 nominal_rate;
- u8 pllbuf[6] = { 0x67 };
+ u16 nominal_rate, input_freq;
+ u8 pllbuf[6] = { 0x67 }, acq_ctl = 0;
+ u16 tps = 0;
+ struct dvb_ofdm_parameters *op = ¶m->u.ofdm;
- /* These settings set "auto-everything" and start the FSM. */
- zl10353_single_write(fe, 0x55, 0x80);
+ zl10353_single_write(fe, RESET, 0x80);
udelay(200);
zl10353_single_write(fe, 0xEA, 0x01);
udelay(200);
zl10353_single_write(fe, 0xEA, 0x00);
- zl10353_single_write(fe, 0x56, 0x28);
- zl10353_single_write(fe, 0x89, 0x20);
- zl10353_single_write(fe, 0x5E, 0x00);
+ zl10353_single_write(fe, AGC_TARGET, 0x28);
+
+ if (op->transmission_mode != TRANSMISSION_MODE_AUTO)
+ acq_ctl |= (1 << 0);
+ if (op->guard_interval != GUARD_INTERVAL_AUTO)
+ acq_ctl |= (1 << 1);
+ zl10353_single_write(fe, ACQ_CTL, acq_ctl);
- zl10353_calc_nominal_rate(fe, param->u.ofdm.bandwidth, &nominal_rate);
+ switch (op->bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ /* These are extrapolated from the 7 and 8MHz values */
+ zl10353_single_write(fe, MCLK_RATIO, 0x97);
+ zl10353_single_write(fe, 0x64, 0x34);
+ break;
+ case BANDWIDTH_7_MHZ:
+ zl10353_single_write(fe, MCLK_RATIO, 0x86);
+ zl10353_single_write(fe, 0x64, 0x35);
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ zl10353_single_write(fe, MCLK_RATIO, 0x75);
+ zl10353_single_write(fe, 0x64, 0x36);
+ }
+
+ zl10353_calc_nominal_rate(fe, op->bandwidth, &nominal_rate);
zl10353_single_write(fe, TRL_NOMINAL_RATE_1, msb(nominal_rate));
zl10353_single_write(fe, TRL_NOMINAL_RATE_0, lsb(nominal_rate));
+ state->bandwidth = op->bandwidth;
+
+ zl10353_calc_input_freq(fe, &input_freq);
+ zl10353_single_write(fe, INPUT_FREQ_1, msb(input_freq));
+ zl10353_single_write(fe, INPUT_FREQ_0, lsb(input_freq));
+
+ /* Hint at TPS settings */
+ switch (op->code_rate_HP) {
+ case FEC_2_3:
+ tps |= (1 << 7);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 7);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 7);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 7);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->code_rate_LP) {
+ case FEC_2_3:
+ tps |= (1 << 4);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 4);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 4);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 4);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ break;
+ case FEC_NONE:
+ if (op->hierarchy_information == HIERARCHY_AUTO ||
+ op->hierarchy_information == HIERARCHY_NONE)
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->constellation) {
+ case QPSK:
+ break;
+ case QAM_AUTO:
+ case QAM_16:
+ tps |= (1 << 13);
+ break;
+ case QAM_64:
+ tps |= (2 << 13);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ case TRANSMISSION_MODE_AUTO:
+ break;
+ case TRANSMISSION_MODE_8K:
+ tps |= (1 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ case GUARD_INTERVAL_AUTO:
+ break;
+ case GUARD_INTERVAL_1_16:
+ tps |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ tps |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ tps |= (3 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->hierarchy_information) {
+ case HIERARCHY_AUTO:
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ tps |= (1 << 10);
+ break;
+ case HIERARCHY_2:
+ tps |= (2 << 10);
+ break;
+ case HIERARCHY_4:
+ tps |= (3 << 10);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ zl10353_single_write(fe, TPS_GIVEN_1, msb(tps));
+ zl10353_single_write(fe, TPS_GIVEN_0, lsb(tps));
- zl10353_single_write(fe, 0x6C, 0xCD);
- zl10353_single_write(fe, 0x6D, 0x7E);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
- // if there is no attached secondary tuner, we call set_params to program
- // a potential tuner attached somewhere else
+ /*
+ * If there is no tuner attached to the secondary I2C bus, we call
+ * set_params to program a potential tuner attached somewhere else.
+ * Otherwise, we update the PLL registers via calc_regs.
+ */
if (state->config.no_tuner) {
if (fe->ops.tuner_ops.set_params) {
fe->ops.tuner_ops.set_params(fe, param);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
}
+ } else if (fe->ops.tuner_ops.calc_regs) {
+ fe->ops.tuner_ops.calc_regs(fe, param, pllbuf + 1, 5);
+ pllbuf[1] <<= 1;
+ zl10353_write(fe, pllbuf, sizeof(pllbuf));
}
- // if pllbuf is defined, retrieve the settings
- if (fe->ops.tuner_ops.calc_regs) {
- fe->ops.tuner_ops.calc_regs(fe, param, pllbuf+1, 5);
- pllbuf[1] <<= 1;
- } else {
- // fake pllbuf settings
- pllbuf[1] = 0x61 << 1;
- pllbuf[2] = 0;
- pllbuf[3] = 0;
- pllbuf[3] = 0;
- pllbuf[4] = 0;
+ zl10353_single_write(fe, 0x5F, 0x13);
+
+ /* If no attached tuner or invalid PLL registers, just start the FSM. */
+ if (state->config.no_tuner || fe->ops.tuner_ops.calc_regs == NULL)
+ zl10353_single_write(fe, FSM_GO, 0x01);
+ else
+ zl10353_single_write(fe, TUNER_GO, 0x01);
+
+ return 0;
+}
+
+static int zl10353_get_parameters(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct zl10353_state *state = fe->demodulator_priv;
+ struct dvb_ofdm_parameters *op = ¶m->u.ofdm;
+ int s6, s9;
+ u16 tps;
+ static const u8 tps_fec_to_api[8] = {
+ FEC_1_2,
+ FEC_2_3,
+ FEC_3_4,
+ FEC_5_6,
+ FEC_7_8,
+ FEC_AUTO,
+ FEC_AUTO,
+ FEC_AUTO
+ };
+
+ s6 = zl10353_read_register(state, STATUS_6);
+ s9 = zl10353_read_register(state, STATUS_9);
+ if (s6 < 0 || s9 < 0)
+ return -EREMOTEIO;
+ if ((s6 & (1 << 5)) == 0 || (s9 & (1 << 4)) == 0)
+ return -EINVAL; /* no FE or TPS lock */
+
+ tps = zl10353_read_register(state, TPS_RECEIVED_1) << 8 |
+ zl10353_read_register(state, TPS_RECEIVED_0);
+
+ op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7];
+ op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7];
+
+ switch ((tps >> 13) & 3) {
+ case 0:
+ op->constellation = QPSK;
+ break;
+ case 1:
+ op->constellation = QAM_16;
+ break;
+ case 2:
+ op->constellation = QAM_64;
+ break;
+ default:
+ op->constellation = QAM_AUTO;
+ break;
}
- // there is no call to _just_ start decoding, so we send the pllbuf anyway
- // even if there isn't a PLL attached to the secondary bus
- zl10353_write(fe, pllbuf, sizeof(pllbuf));
+ op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K :
+ TRANSMISSION_MODE_2K;
- zl10353_single_write(fe, 0x5F, 0x13);
- zl10353_single_write(fe, 0x70, 0x01);
- udelay(250);
- zl10353_single_write(fe, 0xE4, 0x00);
- zl10353_single_write(fe, 0xE5, 0x2A);
- zl10353_single_write(fe, 0xE9, 0x02);
- zl10353_single_write(fe, 0xE7, 0x40);
- zl10353_single_write(fe, 0xE8, 0x10);
+ switch ((tps >> 2) & 3) {
+ case 0:
+ op->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ op->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ op->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ op->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ default:
+ op->guard_interval = GUARD_INTERVAL_AUTO;
+ break;
+ }
+
+ switch ((tps >> 10) & 7) {
+ case 0:
+ op->hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ op->hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ op->hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ op->hierarchy_information = HIERARCHY_4;
+ break;
+ default:
+ op->hierarchy_information = HIERARCHY_AUTO;
+ break;
+ }
+
+ param->frequency = 0;
+ op->bandwidth = state->bandwidth;
+ param->inversion = INVERSION_AUTO;
return 0;
}
@@ -406,6 +654,7 @@ static struct dvb_frontend_ops zl10353_ops = {
.write = zl10353_write,
.set_frontend = zl10353_set_parameters,
+ .get_frontend = zl10353_get_parameters,
.get_tune_settings = zl10353_get_tune_settings,
.read_status = zl10353_read_status,
diff --git a/trunk/drivers/media/dvb/frontends/zl10353.h b/trunk/drivers/media/dvb/frontends/zl10353.h
index 1c3d494a6da9..fc734c22b5fa 100644
--- a/trunk/drivers/media/dvb/frontends/zl10353.h
+++ b/trunk/drivers/media/dvb/frontends/zl10353.h
@@ -1,7 +1,7 @@
/*
* Driver for Zarlink DVB-T ZL10353 demodulator
*
- * Copyright (C) 2006 Christopher Pascoe
+ * Copyright (C) 2006, 2007 Christopher Pascoe
*
* 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
@@ -29,8 +29,9 @@ struct zl10353_config
/* demodulator's I2C address */
u8 demod_address;
- /* frequencies in kHz */
- int adc_clock; /* default: 45056 */
+ /* frequencies in units of 0.1kHz */
+ int adc_clock; /* default: 450560 (45.056 MHz) */
+ int if2; /* default: 361667 (36.1667 MHz) */
/* set if no pll is connected to the secondary i2c bus */
int no_tuner;
@@ -49,6 +50,6 @@ static inline struct dvb_frontend* zl10353_attach(const struct zl10353_config *c
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
return NULL;
}
-#endif // CONFIG_DVB_ZL10353
+#endif /* CONFIG_DVB_ZL10353 */
#endif /* ZL10353_H */
diff --git a/trunk/drivers/media/dvb/frontends/zl10353_priv.h b/trunk/drivers/media/dvb/frontends/zl10353_priv.h
index 4962434b35e7..055ff1f7e349 100644
--- a/trunk/drivers/media/dvb/frontends/zl10353_priv.h
+++ b/trunk/drivers/media/dvb/frontends/zl10353_priv.h
@@ -1,7 +1,7 @@
/*
* Driver for Zarlink DVB-T ZL10353 demodulator
*
- * Copyright (C) 2006 Christopher Pascoe
+ * Copyright (C) 2006, 2007 Christopher Pascoe
*
* 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ZL10353_PRIV_
@@ -46,9 +46,28 @@ enum zl10353_reg_addr {
RS_ERR_CNT_0 = 0x13,
RS_UBC_1 = 0x14,
RS_UBC_0 = 0x15,
+ TPS_RECEIVED_1 = 0x1D,
+ TPS_RECEIVED_0 = 0x1E,
+ TPS_CURRENT_1 = 0x1F,
+ TPS_CURRENT_0 = 0x20,
+ RESET = 0x55,
+ AGC_TARGET = 0x56,
+ MCLK_RATIO = 0x5C,
+ ACQ_CTL = 0x5E,
TRL_NOMINAL_RATE_1 = 0x65,
TRL_NOMINAL_RATE_0 = 0x66,
+ INPUT_FREQ_1 = 0x6C,
+ INPUT_FREQ_0 = 0x6D,
+ TPS_GIVEN_1 = 0x6E,
+ TPS_GIVEN_0 = 0x6F,
+ TUNER_GO = 0x70,
+ FSM_GO = 0x71,
CHIP_ID = 0x7F,
+ CHAN_STEP_1 = 0xE4,
+ CHAN_STEP_0 = 0xE5,
+ OFDM_LOCK_TIME = 0xE7,
+ FEC_LOCK_TIME = 0xE8,
+ ACQ_DELAY = 0xE9,
};
#endif /* _ZL10353_PRIV_ */
diff --git a/trunk/drivers/media/dvb/ttpci/Kconfig b/trunk/drivers/media/dvb/ttpci/Kconfig
index 54b91f26ca63..ae882432dd3d 100644
--- a/trunk/drivers/media/dvb/ttpci/Kconfig
+++ b/trunk/drivers/media/dvb/ttpci/Kconfig
@@ -1,8 +1,14 @@
+config TTPCI_EEPROM
+ tristate
+ default n
+
config DVB_AV7110
tristate "AV7110 cards"
- depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
+ depends on DVB_CORE && PCI && I2C
select FW_LOADER if !DVB_AV7110_FIRMWARE
+ select TTPCI_EEPROM
select VIDEO_SAA7146_VV
+ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
select DVB_VES1820 if !DVB_FE_CUSTOMISE
select DVB_VES1X93 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
@@ -57,10 +63,19 @@ config DVB_AV7110_OSD
All other people say N.
+config DVB_BUDGET_CORE
+ tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)"
+ depends on DVB_CORE && PCI && I2C
+ select VIDEO_SAA7146
+ select TTPCI_EEPROM
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder.
+
config DVB_BUDGET
tristate "Budget cards"
- depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
- select VIDEO_SAA7146
+ depends on DVB_BUDGET_CORE && I2C
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_VES1X93 if !DVB_FE_CUSTOMISE
select DVB_VES1820 if !DVB_FE_CUSTOMISE
@@ -73,9 +88,9 @@ config DVB_BUDGET
select DVB_TDA826X if !DVB_FE_CUSTOMISE
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
help
- Support for simple SAA7146 based DVB cards
- (so called Budget- or Nova-PCI cards) without onboard
- MPEG2 decoder.
+ Support for simple SAA7146 based DVB cards (so called Budget-
+ or Nova-PCI cards) without onboard MPEG2 decoder, and without
+ analog inputs or an onboard Common Interface connector.
Say Y if you own such a card and want to use it.
@@ -84,8 +99,7 @@ config DVB_BUDGET
config DVB_BUDGET_CI
tristate "Budget cards with onboard CI connector"
- depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 && INPUT
- select VIDEO_SAA7146
+ depends on DVB_BUDGET_CORE && I2C
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
@@ -106,8 +120,9 @@ config DVB_BUDGET_CI
config DVB_BUDGET_AV
tristate "Budget cards with analog video inputs"
- depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
+ depends on DVB_BUDGET_CORE && I2C
select VIDEO_SAA7146_VV
+ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
select DVB_PLL if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
@@ -127,8 +142,8 @@ config DVB_BUDGET_AV
config DVB_BUDGET_PATCH
tristate "AV7110 cards with Budget Patch"
- depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1
- select DVB_AV7110
+ depends on DVB_BUDGET_CORE && I2C
+ depends on DVB_AV7110
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_VES1X93 if !DVB_FE_CUSTOMISE
select DVB_TDA8083 if !DVB_FE_CUSTOMISE
diff --git a/trunk/drivers/media/dvb/ttpci/Makefile b/trunk/drivers/media/dvb/ttpci/Makefile
index 2c1145236ee6..d7483f1a9b3f 100644
--- a/trunk/drivers/media/dvb/ttpci/Makefile
+++ b/trunk/drivers/media/dvb/ttpci/Makefile
@@ -5,11 +5,13 @@
dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o av7110_ir.o
-obj-$(CONFIG_DVB_BUDGET) += budget-core.o budget.o ttpci-eeprom.o
-obj-$(CONFIG_DVB_BUDGET_AV) += budget-core.o budget-av.o ttpci-eeprom.o
-obj-$(CONFIG_DVB_BUDGET_CI) += budget-core.o budget-ci.o ttpci-eeprom.o
-obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-core.o budget-patch.o ttpci-eeprom.o
-obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o ttpci-eeprom.o
+obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
+obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o
+obj-$(CONFIG_DVB_BUDGET) += budget.o
+obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o
+obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o
+obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o
+obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/trunk/drivers/media/dvb/ttpci/av7110.c b/trunk/drivers/media/dvb/ttpci/av7110.c
index 0d36c155695b..0e5701bdff19 100644
--- a/trunk/drivers/media/dvb/ttpci/av7110.c
+++ b/trunk/drivers/media/dvb/ttpci/av7110.c
@@ -2595,7 +2595,8 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
mutex_init(&av7110->osd_mutex);
/* TV standard */
- av7110->vidmode = tv_standard == 1 ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL;
+ av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC
+ : AV7110_VIDEO_MODE_PAL;
/* ARM "watchdog" */
init_waitqueue_head(&av7110->arm_wait);
diff --git a/trunk/drivers/media/dvb/ttpci/av7110.h b/trunk/drivers/media/dvb/ttpci/av7110.h
index 0cb439527498..39fbf7d5cffb 100644
--- a/trunk/drivers/media/dvb/ttpci/av7110.h
+++ b/trunk/drivers/media/dvb/ttpci/av7110.h
@@ -46,6 +46,11 @@ extern int av7110_debug;
enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM};
+enum av7110_video_mode {
+ AV7110_VIDEO_MODE_PAL = 0,
+ AV7110_VIDEO_MODE_NTSC = 1
+};
+
struct av7110_p2t {
u8 pes[TS_SIZE];
u8 counter;
@@ -170,7 +175,7 @@ struct av7110 {
ca_slot_info_t ci_slot[2];
- int vidmode;
+ enum av7110_video_mode vidmode;
struct dmxdev dmxdev;
struct dvb_demux demux;
diff --git a/trunk/drivers/media/dvb/ttpci/av7110_av.c b/trunk/drivers/media/dvb/ttpci/av7110_av.c
index d75e7e48addc..aef6e36d7c5c 100644
--- a/trunk/drivers/media/dvb/ttpci/av7110_av.c
+++ b/trunk/drivers/media/dvb/ttpci/av7110_av.c
@@ -329,7 +329,7 @@ int av7110_set_volume(struct av7110 *av7110, int volleft, int volright)
return 0;
}
-int av7110_set_vidmode(struct av7110 *av7110, int mode)
+int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode)
{
int ret;
dprintk(2, "av7110:%p, \n", av7110);
@@ -348,11 +348,15 @@ int av7110_set_vidmode(struct av7110 *av7110, int mode)
}
-static int sw2mode[16] = {
- VIDEO_MODE_PAL, VIDEO_MODE_NTSC, VIDEO_MODE_NTSC, VIDEO_MODE_PAL,
- VIDEO_MODE_NTSC, VIDEO_MODE_NTSC, VIDEO_MODE_PAL, VIDEO_MODE_NTSC,
- VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL,
- VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL,
+static enum av7110_video_mode sw2mode[16] = {
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC,
+ AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL,
+ AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL,
};
static int get_video_format(struct av7110 *av7110, u8 *buf, int count)
diff --git a/trunk/drivers/media/dvb/ttpci/av7110_av.h b/trunk/drivers/media/dvb/ttpci/av7110_av.h
index 45dc144b8b43..5f02ef85e47d 100644
--- a/trunk/drivers/media/dvb/ttpci/av7110_av.h
+++ b/trunk/drivers/media/dvb/ttpci/av7110_av.h
@@ -3,7 +3,8 @@
struct av7110;
-extern int av7110_set_vidmode(struct av7110 *av7110, int mode);
+extern int av7110_set_vidmode(struct av7110 *av7110,
+ enum av7110_video_mode mode);
extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len);
extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen);
diff --git a/trunk/drivers/media/dvb/ttpci/av7110_v4l.c b/trunk/drivers/media/dvb/ttpci/av7110_v4l.c
index 76cca003252f..e2f066fb7967 100644
--- a/trunk/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/trunk/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -876,11 +876,11 @@ static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
if (std->id & V4L2_STD_PAL) {
- av7110->vidmode = VIDEO_MODE_PAL;
+ av7110->vidmode = AV7110_VIDEO_MODE_PAL;
av7110_set_vidmode(av7110, av7110->vidmode);
}
else if (std->id & V4L2_STD_NTSC) {
- av7110->vidmode = VIDEO_MODE_NTSC;
+ av7110->vidmode = AV7110_VIDEO_MODE_NTSC;
av7110_set_vidmode(av7110, av7110->vidmode);
}
else
diff --git a/trunk/drivers/media/radio/Kconfig b/trunk/drivers/media/radio/Kconfig
index 11e962f1a97f..8d5214f18cf0 100644
--- a/trunk/drivers/media/radio/Kconfig
+++ b/trunk/drivers/media/radio/Kconfig
@@ -351,4 +351,14 @@ config USB_DSBR
To compile this driver as a module, choose M here: the
module will be called dsbr100.
+config USB_SI470X
+ tristate "Silicon Labs Si470x FM Radio Receiver support"
+ depends on USB && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-silabs.
+
endif # RADIO_ADAPTERS
diff --git a/trunk/drivers/media/radio/Makefile b/trunk/drivers/media/radio/Makefile
index cf55a18e3ddf..a30159f6fa42 100644
--- a/trunk/drivers/media/radio/Makefile
+++ b/trunk/drivers/media/radio/Makefile
@@ -21,5 +21,6 @@ obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
+obj-$(CONFIG_USB_SI470X) += radio-si470x.o
EXTRA_CFLAGS += -Isound
diff --git a/trunk/drivers/media/radio/dsbr100.c b/trunk/drivers/media/radio/dsbr100.c
index 3bd07f7e3774..36c0e3651502 100644
--- a/trunk/drivers/media/radio/dsbr100.c
+++ b/trunk/drivers/media/radio/dsbr100.c
@@ -33,6 +33,9 @@
History:
+ Version 0.43:
+ Oliver Neukum: avoided DMA coherency issue
+
Version 0.42:
Converted dsbr100 to use video_ioctl2
by Douglas Landgraf
@@ -135,7 +138,7 @@ module_param(radio_nr, int, 0);
struct dsbr100_device {
struct usb_device *usbdev;
struct video_device *videodev;
- unsigned char transfer_buffer[TB_LEN];
+ u8 *transfer_buffer;
int curfreq;
int stereo;
int users;
@@ -237,10 +240,7 @@ static void dsbr100_getstat(struct dsbr100_device *radio)
/* handle unplugging of the device, release data structures
if nothing keeps us from doing it. If something is still
keeping us busy, the release callback of v4l will take care
-of releasing it. stv680.c does not relase its private
-data, so I don't do this here either. Checking out the
-code I'd expect I better did that, but if there's a memory
-leak here it's tiny (~50 bytes per disconnect) */
+of releasing it. */
static void usb_dsbr100_disconnect(struct usb_interface *intf)
{
struct dsbr100_device *radio = usb_get_intfdata(intf);
@@ -250,6 +250,7 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf)
video_unregister_device(radio->videodev);
radio->videodev = NULL;
if (radio->users) {
+ kfree(radio->transfer_buffer);
kfree(radio);
} else {
radio->removed = 1;
@@ -425,6 +426,7 @@ static int usb_dsbr100_close(struct inode *inode, struct file *file)
return -ENODEV;
radio->users = 0;
if (radio->removed) {
+ kfree(radio->transfer_buffer);
kfree(radio);
}
return 0;
@@ -471,7 +473,12 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL)))
return -ENOMEM;
+ if (!(radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL))) {
+ kfree(radio);
+ return -ENOMEM;
+ }
if (!(radio->videodev = video_device_alloc())) {
+ kfree(radio->transfer_buffer);
kfree(radio);
return -ENOMEM;
}
@@ -485,6 +492,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
if (video_register_device(radio->videodev, VFL_TYPE_RADIO,radio_nr)) {
warn("Could not register video device");
video_device_release(radio->videodev);
+ kfree(radio->transfer_buffer);
kfree(radio);
return -EIO;
}
diff --git a/trunk/drivers/media/radio/radio-gemtek.c b/trunk/drivers/media/radio/radio-gemtek.c
index 5e4b9ddb23c0..246422b49267 100644
--- a/trunk/drivers/media/radio/radio-gemtek.c
+++ b/trunk/drivers/media/radio/radio-gemtek.c
@@ -58,10 +58,10 @@ static int initmute = 1;
static int radio_nr = -1;
module_param(io, int, 0444);
-MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic"
+MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
"probing is disabled or fails. The most common I/O ports are: 0x20c "
"0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
- " work for the combined sound/radiocard).");
+ "work for the combined sound/radiocard).");
module_param(probe, bool, 0444);
MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most "
@@ -392,7 +392,7 @@ static struct v4l2_queryctrl radio_qctrl[] = {
}
};
-static struct file_operations gemtek_fops = {
+static const struct file_operations gemtek_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
diff --git a/trunk/drivers/media/radio/radio-maestro.c b/trunk/drivers/media/radio/radio-maestro.c
index 8e33a19a22a3..bc51f4d23a5a 100644
--- a/trunk/drivers/media/radio/radio-maestro.c
+++ b/trunk/drivers/media/radio/radio-maestro.c
@@ -423,7 +423,7 @@ static int __devinit maestro_probe(struct pci_dev *pdev,
errunr:
video_unregister_device(maestro_radio_inst);
errfr1:
- kfree(maestro_radio_inst);
+ video_device_release(maestro_radio_inst);
errfr:
kfree(radio_unit);
err:
diff --git a/trunk/drivers/media/radio/radio-sf16fmi.c b/trunk/drivers/media/radio/radio-sf16fmi.c
index 395165367f37..3118bdab3183 100644
--- a/trunk/drivers/media/radio/radio-sf16fmi.c
+++ b/trunk/drivers/media/radio/radio-sf16fmi.c
@@ -321,7 +321,7 @@ static struct isapnp_device_id id_table[] __devinitdata = {
MODULE_DEVICE_TABLE(isapnp, id_table);
-static int isapnp_fmi_probe(void)
+static int __init isapnp_fmi_probe(void)
{
int i = 0;
diff --git a/trunk/drivers/media/radio/radio-sf16fmr2.c b/trunk/drivers/media/radio/radio-sf16fmr2.c
index c432c44bd634..f7c8b000404f 100644
--- a/trunk/drivers/media/radio/radio-sf16fmr2.c
+++ b/trunk/drivers/media/radio/radio-sf16fmr2.c
@@ -476,8 +476,7 @@ static int __init fmr2_init(void)
return -EBUSY;
}
- if(video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
- {
+ if (video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
release_region(io, 2);
return -EINVAL;
}
diff --git a/trunk/drivers/media/radio/radio-si470x.c b/trunk/drivers/media/radio/radio-si470x.c
new file mode 100644
index 000000000000..8e4bd4769048
--- /dev/null
+++ b/trunk/drivers/media/radio/radio-si470x.c
@@ -0,0 +1,1432 @@
+/*
+ * drivers/media/radio/radio-si470x.c
+ *
+ * Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers:
+ * - Silicon Labs USB FM Radio Reference Design
+ * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF)
+ *
+ * Copyright (c) 2008 Tobias Lorenz
+ *
+ * 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
+ */
+
+
+/*
+ * History:
+ * 2008-01-12 Tobias Lorenz
+ * Version 1.0.0
+ * - First working version
+ * 2008-01-13 Tobias Lorenz
+ * Version 1.0.1
+ * - Improved error handling, every function now returns errno
+ * - Improved multi user access (start/mute/stop)
+ * - Channel doesn't get lost anymore after start/mute/stop
+ * - RDS support added (polling mode via interrupt EP 1)
+ * - marked default module parameters with *value*
+ * - switched from bit structs to bit masks
+ * - header file cleaned and integrated
+ * 2008-01-14 Tobias Lorenz
+ * Version 1.0.2
+ * - hex values are now lower case
+ * - commented USB ID for ADS/Tech moved on todo list
+ * - blacklisted si470x in hid-quirks.c
+ * - rds buffer handling functions integrated into *_work, *_read
+ * - rds_command in si470x_poll exchanged against simple retval
+ * - check for firmware version 15
+ * - code order and prototypes still remain the same
+ * - spacing and bottom of band codes remain the same
+ * 2008-01-16 Tobias Lorenz
+ * Version 1.0.3
+ * - code reordered to avoid function prototypes
+ * - switch/case defaults are now more user-friendly
+ * - unified comment style
+ * - applied all checkpatch.pl v1.12 suggestions
+ * except the warning about the too long lines with bit comments
+ * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
+ * 2008-01-22 Tobias Lorenz
+ * Version 1.0.4
+ * - avoid poss. locking when doing copy_to_user which may sleep
+ * - RDS is automatically activated on read now
+ * - code cleaned of unnecessary rds_commands
+ * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
+ * (thanks to Guillaume RAMOUSSE)
+ *
+ * ToDo:
+ * - add seeking support
+ * - add firmware download/update support
+ * - RDS support: interrupt mode, instead of polling
+ * - add LED status output (check if that's not already done in firmware)
+ */
+
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Tobias Lorenz "
+#define DRIVER_NAME "radio-si470x"
+#define DRIVER_VERSION KERNEL_VERSION(1, 0, 4)
+#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
+
+
+/* kernel includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/* USB Device ID List */
+static struct usb_device_id si470x_usb_driver_id_table[] = {
+ /* Silicon Labs USB FM Radio Reference Design */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
+ /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+/* Spacing (kHz) */
+/* 0: 200 kHz (USA, Australia) */
+/* 1: 100 kHz (Europe, Japan) */
+/* 2: 50 kHz */
+static int space = 2;
+module_param(space, int, 0);
+MODULE_PARM_DESC(radio_nr, "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 int band = 1;
+module_param(band, int, 0);
+MODULE_PARM_DESC(radio_nr, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
+
+/* De-emphasis */
+/* 0: 75 us (USA) */
+/* 1: 50 us (Europe, Australia, Japan) */
+static int de = 1;
+module_param(de, int, 0);
+MODULE_PARM_DESC(radio_nr, "De-emphasis: 0=75us *1=50us*");
+
+/* USB timeout */
+static int usb_timeout = 500;
+module_param(usb_timeout, int, 0);
+MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
+
+/* Seek retries */
+static int seek_retries = 100;
+module_param(seek_retries, int, 0);
+MODULE_PARM_DESC(seek_retries, "Seek retries: *100*");
+
+/* RDS buffer blocks */
+static int rds_buf = 100;
+module_param(rds_buf, int, 0);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+
+/* RDS maximum block errors */
+static int max_rds_errors = 1;
+/* 0 means 0 errors requiring correction */
+/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
+/* 2 means 3-5 errors requiring correction */
+/* 3 means 6+ errors or errors in checkword, correction not possible */
+module_param(max_rds_errors, int, 0);
+MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
+
+/* RDS poll frequency */
+static int rds_poll_time = 40;
+/* 40 is used by the original USBRadio.exe */
+/* 50 is used by radio-cadet */
+/* 75 should be okay */
+/* 80 is the usual RDS receive interval */
+module_param(rds_poll_time, int, 0);
+MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*");
+
+
+
+/**************************************************************************
+ * Register Definitions
+ **************************************************************************/
+#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */
+#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */
+#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */
+
+#define DEVICEID 0 /* Device ID */
+#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */
+#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */
+
+#define CHIPID 1 /* Chip ID */
+#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */
+#define CHIPID_DEV 0x0200 /* bits 09..09: Device */
+#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */
+
+#define POWERCFG 2 /* Power Configuration */
+#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */
+#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */
+#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */
+#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */
+#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */
+#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */
+#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */
+#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */
+#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */
+
+#define CHANNEL 3 /* Channel */
+#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */
+#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */
+
+#define SYSCONFIG1 4 /* System Configuration 1 */
+#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */
+#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */
+#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */
+#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */
+#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */
+#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */
+#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */
+#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */
+#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */
+
+#define SYSCONFIG2 5 /* System Configuration 2 */
+#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */
+#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */
+#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */
+#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */
+
+#define SYSCONFIG3 6 /* System Configuration 3 */
+#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */
+#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */
+#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */
+#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */
+
+#define TEST1 7 /* Test 1 */
+#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */
+
+#define TEST2 8 /* Test 2 */
+/* TEST2 only contains reserved bits */
+
+#define BOOTCONFIG 9 /* Boot Configuration */
+/* BOOTCONFIG only contains reserved bits */
+
+#define STATUSRSSI 10 /* Status RSSI */
+#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */
+#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */
+#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */
+#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */
+#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */
+#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */
+#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */
+#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */
+
+#define READCHAN 11 /* Read Channel */
+#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */
+#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */
+#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */
+#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */
+
+#define RDSA 12 /* RDSA */
+#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */
+
+#define RDSB 13 /* RDSB */
+#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */
+
+#define RDSC 14 /* RDSC */
+#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */
+
+#define RDSD 15 /* RDSD */
+#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */
+
+
+
+/**************************************************************************
+ * USB HID Reports
+ **************************************************************************/
+
+/* Reports 1-16 give direct read/write access to the 16 Si470x registers */
+/* with the (REPORT_ID - 1) corresponding to the register address across USB */
+/* endpoint 0 using GET_REPORT and SET_REPORT */
+#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1)
+#define REGISTER_REPORT(reg) ((reg) + 1)
+
+/* Report 17 gives direct read/write access to the entire Si470x register */
+/* map across endpoint 0 using GET_REPORT and SET_REPORT */
+#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define ENTIRE_REPORT 17
+
+/* Report 18 is used to send the lowest 6 Si470x registers up the HID */
+/* interrupt endpoint 1 to Windows every 20 milliseconds for status */
+#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define RDS_REPORT 18
+
+/* Report 19: LED state */
+#define LED_REPORT_SIZE 3
+#define LED_REPORT 19
+
+/* Report 19: stream */
+#define STREAM_REPORT_SIZE 3
+#define STREAM_REPORT 19
+
+/* Report 20: scratch */
+#define SCRATCH_PAGE_SIZE 63
+#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1)
+#define SCRATCH_REPORT 20
+
+/* Reports 19-22: flash upgrade of the C8051F321 */
+#define WRITE_REPORT 19
+#define FLASH_REPORT 20
+#define CRC_REPORT 21
+#define RESPONSE_REPORT 22
+
+/* Report 23: currently unused, but can accept 60 byte reports on the HID */
+/* interrupt out endpoint 2 every 1 millisecond */
+#define UNUSED_REPORT 23
+
+
+
+/**************************************************************************
+ * Software/Hardware Versions
+ **************************************************************************/
+#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6
+#define RADIO_SW_VERSION 7
+#define RADIO_SW_VERSION_CURRENT 15
+#define RADIO_HW_VERSION 1
+
+#define SCRATCH_PAGE_SW_VERSION 1
+#define SCRATCH_PAGE_HW_VERSION 2
+
+
+
+/**************************************************************************
+ * LED State Definitions
+ **************************************************************************/
+#define LED_COMMAND 0x35
+
+#define NO_CHANGE_LED 0x00
+#define ALL_COLOR_LED 0x01 /* streaming state */
+#define BLINK_GREEN_LED 0x02 /* connect state */
+#define BLINK_RED_LED 0x04
+#define BLINK_ORANGE_LED 0x10 /* disconnect state */
+#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */
+#define SOLID_RED_LED 0x40 /* bootload state */
+#define SOLID_ORANGE_LED 0x80
+
+
+
+/**************************************************************************
+ * Stream State Definitions
+ **************************************************************************/
+#define STREAM_COMMAND 0x36
+#define STREAM_VIDPID 0x00
+#define STREAM_AUDIO 0xff
+
+
+
+/**************************************************************************
+ * Bootloader / Flash Commands
+ **************************************************************************/
+
+/* unique id sent to bootloader and required to put into a bootload state */
+#define UNIQUE_BL_ID 0x34
+
+/* mask for the flash data */
+#define FLASH_DATA_MASK 0x55
+
+/* bootloader commands */
+#define GET_SW_VERSION_COMMAND 0x00
+#define SET_PAGE_COMMAND 0x01
+#define ERASE_PAGE_COMMAND 0x02
+#define WRITE_PAGE_COMMAND 0x03
+#define CRC_ON_PAGE_COMMAND 0x04
+#define READ_FLASH_BYTE_COMMAND 0x05
+#define RESET_DEVICE_COMMAND 0x06
+#define GET_HW_VERSION_COMMAND 0x07
+#define BLANK 0xff
+
+/* bootloader command responses */
+#define COMMAND_OK 0x01
+#define COMMAND_FAILED 0x02
+#define COMMAND_PENDING 0x03
+
+/* buffer sizes */
+#define COMMAND_BUFFER_SIZE 4
+#define RESPONSE_BUFFER_SIZE 2
+#define FLASH_BUFFER_SIZE 64
+#define CRC_BUFFER_SIZE 3
+
+
+
+/**************************************************************************
+ * General Driver Definitions
+ **************************************************************************/
+
+/*
+ * si470x_device - private data
+ */
+struct si470x_device {
+ /* reference to USB and video device */
+ struct usb_device *usbdev;
+ struct video_device *videodev;
+
+ /* are these really necessary ? */
+ int users;
+
+ /* report buffer (maximum 64 bytes) */
+ unsigned char buf[64];
+
+ /* Silabs internal registers (0..15) */
+ unsigned short registers[RADIO_REGISTER_NUM];
+
+ /* RDS receive buffer */
+ struct work_struct work;
+ wait_queue_head_t read_queue;
+ struct timer_list timer;
+ spinlock_t lock; /* buffer locking */
+ unsigned char *buffer; /* size is always multiple of three */
+ unsigned int buf_size;
+ unsigned int rd_index;
+ unsigned int wr_index;
+};
+
+
+/*
+ * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
+ * 62.5 kHz otherwise.
+ * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
+ * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
+ * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
+ */
+#define FREQ_MUL (1000000 / 62.5)
+
+
+
+/**************************************************************************
+ * General Driver Functions
+ **************************************************************************/
+
+/*
+ * si470x_get_report - receive a HID report
+ */
+static int si470x_get_report(struct si470x_device *radio, int size)
+{
+ return usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ HID_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ radio->buf[0], 2,
+ radio->buf, size, usb_timeout);
+}
+
+
+/*
+ * si470x_set_report - send a HID report
+ */
+static int si470x_set_report(struct si470x_device *radio, int size)
+{
+ return usb_control_msg(radio->usbdev,
+ usb_sndctrlpipe(radio->usbdev, 0),
+ HID_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ radio->buf[0], 2,
+ radio->buf, size, usb_timeout);
+}
+
+
+/*
+ * si470x_get_register - read register
+ */
+static int si470x_get_register(struct si470x_device *radio, int regnr)
+{
+ int retval;
+
+ radio->buf[0] = REGISTER_REPORT(regnr);
+
+ retval = si470x_get_report(radio, REGISTER_REPORT_SIZE);
+ if (retval >= 0)
+ radio->registers[regnr] = (radio->buf[1] << 8) | radio->buf[2];
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_set_register - write register
+ */
+static int si470x_set_register(struct si470x_device *radio, int regnr)
+{
+ int retval;
+
+ radio->buf[0] = REGISTER_REPORT(regnr);
+ radio->buf[1] = (radio->registers[regnr] & 0xff00) >> 8;
+ radio->buf[2] = (radio->registers[regnr] & 0x00ff);
+
+ retval = si470x_set_report(radio, REGISTER_REPORT_SIZE);
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_get_all_registers - read entire registers
+ */
+static int si470x_get_all_registers(struct si470x_device *radio)
+{
+ int retval;
+ int regnr;
+
+ radio->buf[0] = ENTIRE_REPORT;
+
+ retval = si470x_get_report(radio, ENTIRE_REPORT_SIZE);
+
+ if (retval >= 0)
+ for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
+ radio->registers[regnr] =
+ (radio->buf[regnr * RADIO_REGISTER_SIZE + 1] << 8) |
+ radio->buf[regnr * RADIO_REGISTER_SIZE + 2];
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_get_rds_registers - read rds registers
+ */
+static int si470x_get_rds_registers(struct si470x_device *radio)
+{
+ int retval;
+ int regnr;
+ int size;
+
+ radio->buf[0] = RDS_REPORT;
+
+ retval = usb_interrupt_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 1),
+ radio->buf, RDS_REPORT_SIZE, &size, usb_timeout);
+
+ if (retval >= 0)
+ for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+ radio->registers[STATUSRSSI + regnr] =
+ (radio->buf[regnr * RADIO_REGISTER_SIZE + 1] << 8) |
+ radio->buf[regnr * RADIO_REGISTER_SIZE + 2];
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_set_chan - set the channel
+ */
+static int si470x_set_chan(struct si470x_device *radio, int chan)
+{
+ int retval, i;
+
+ /* start tuning */
+ radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
+ radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
+ retval = si470x_set_register(radio, CHANNEL);
+ if (retval < 0)
+ return retval;
+
+ /* wait till seek operation has completed */
+ i = 0;
+ do {
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ return retval;
+ } while ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) &&
+ (++i < seek_retries));
+ if (i >= seek_retries)
+ printk(KERN_WARNING DRIVER_NAME
+ ": seek does not finish after %d tries\n", i);
+
+ /* stop tuning */
+ radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
+ return si470x_set_register(radio, CHANNEL);
+}
+
+
+/*
+ * si470x_get_freq - get the frequency
+ */
+static int si470x_get_freq(struct si470x_device *radio)
+{
+ int spacing, band_bottom, chan, freq;
+ int retval;
+
+ /* Spacing (kHz) */
+ switch (space) {
+ /* 0: 200 kHz (USA, Australia) */
+ case 0 : spacing = 0.200 * FREQ_MUL; break;
+ /* 1: 100 kHz (Europe, Japan) */
+ case 1 : spacing = 0.100 * FREQ_MUL; break;
+ /* 2: 50 kHz */
+ default: spacing = 0.050 * FREQ_MUL; break;
+ };
+
+ /* Bottom of Band (MHz) */
+ switch (band) {
+ /* 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);
+ if (retval < 0)
+ return retval;
+ chan = radio->registers[READCHAN] & READCHAN_READCHAN;
+
+ /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
+ freq = chan * spacing + band_bottom;
+
+ return freq;
+}
+
+
+/*
+ * si470x_set_freq - set the frequency
+ */
+static int si470x_set_freq(struct si470x_device *radio, int freq)
+{
+ int spacing, band_bottom, chan;
+
+ /* Spacing (kHz) */
+ switch (space) {
+ /* 0: 200 kHz (USA, Australia) */
+ case 0 : spacing = 0.200 * FREQ_MUL; break;
+ /* 1: 100 kHz (Europe, Japan) */
+ case 1 : spacing = 0.100 * FREQ_MUL; break;
+ /* 2: 50 kHz */
+ default: spacing = 0.050 * FREQ_MUL; break;
+ };
+
+ /* Bottom of Band (MHz) */
+ switch (band) {
+ /* 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;
+ };
+
+ /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
+ chan = (freq - band_bottom) / spacing;
+
+ return si470x_set_chan(radio, chan);
+}
+
+
+/*
+ * si470x_start - switch on radio
+ */
+static int si470x_start(struct si470x_device *radio)
+{
+ int retval;
+
+ /* powercfg */
+ radio->registers[POWERCFG] =
+ POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
+ retval = si470x_set_register(radio, POWERCFG);
+ if (retval < 0)
+ return retval;
+
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ return retval;
+
+ /* sysconfig 2 */
+ radio->registers[SYSCONFIG2] =
+ (0x3f << 8) | /* SEEKTH */
+ (band << 6) | /* BAND */
+ (space << 4) | /* SPACE */
+ 15; /* VOLUME (max) */
+ retval = si470x_set_register(radio, SYSCONFIG2);
+ if (retval < 0)
+ return retval;
+
+ /* reset last channel */
+ return si470x_set_chan(radio,
+ radio->registers[CHANNEL] & CHANNEL_CHAN);
+}
+
+
+/*
+ * si470x_stop - switch off radio
+ */
+static int si470x_stop(struct si470x_device *radio)
+{
+ int retval;
+
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ return retval;
+
+ /* powercfg */
+ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+ /* POWERCFG_ENABLE has to automatically go low */
+ radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
+ return si470x_set_register(radio, POWERCFG);
+}
+
+
+/*
+ * si470x_rds_on - switch on rds reception
+ */
+static int si470x_rds_on(struct si470x_device *radio)
+{
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
+ return si470x_set_register(radio, SYSCONFIG1);
+}
+
+
+
+/**************************************************************************
+ * RDS Driver Functions
+ **************************************************************************/
+
+/*
+ * si470x_rds - rds processing function
+ */
+static void si470x_rds(struct si470x_device *radio)
+{
+ unsigned char tmpbuf[3];
+ unsigned char blocknum;
+ unsigned char bler; /* rds block errors */
+ unsigned short rds;
+ unsigned int i;
+
+ /* get rds blocks */
+ if (si470x_get_rds_registers(radio) < 0)
+ return;
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
+ /* No RDS group ready */
+ return;
+ }
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {
+ /* RDS decoder not synchronized */
+ return;
+ }
+
+ /* copy four RDS blocks to internal buffer */
+ if (spin_trylock(&radio->lock)) {
+ /* process each rds block */
+ for (blocknum = 0; blocknum < 4; blocknum++) {
+ switch (blocknum) {
+ default:
+ bler = (radio->registers[STATUSRSSI] &
+ STATUSRSSI_BLERA) >> 9;
+ rds = radio->registers[RDSA];
+ break;
+ case 1:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERB) >> 14;
+ rds = radio->registers[RDSB];
+ break;
+ case 2:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERC) >> 12;
+ rds = radio->registers[RDSC];
+ break;
+ case 3:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERD) >> 10;
+ rds = radio->registers[RDSD];
+ break;
+ };
+
+ /* Fill the V4L2 RDS buffer */
+ tmpbuf[0] = rds & 0x00ff; /* LSB */
+ tmpbuf[1] = (rds & 0xff00) >> 8;/* MSB */
+ tmpbuf[2] = blocknum; /* offset name */
+ tmpbuf[2] |= blocknum << 3; /* received offset */
+ if (bler > max_rds_errors)
+ tmpbuf[2] |= 0x80; /* uncorrectable errors */
+ else if (bler > 0)
+ tmpbuf[2] |= 0x40; /* corrected error(s) */
+
+ /* copy RDS block to internal buffer */
+ for (i = 0; i < 3; i++) {
+ radio->buffer[radio->wr_index] = tmpbuf[i];
+ radio->wr_index++;
+ }
+
+ /* wrap write pointer */
+ if (radio->wr_index >= radio->buf_size)
+ radio->wr_index = 0;
+
+ /* check for overflow */
+ if (radio->wr_index == radio->rd_index) {
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+ }
+ }
+ spin_unlock(&radio->lock);
+ }
+
+ /* wake up read queue */
+ if (radio->wr_index != radio->rd_index)
+ wake_up_interruptible(&radio->read_queue);
+}
+
+
+/*
+ * si470x_timer - rds timer function
+ */
+static void si470x_timer(unsigned long data)
+{
+ struct si470x_device *radio = (struct si470x_device *) data;
+
+ schedule_work(&radio->work);
+}
+
+
+/*
+ * si470x_work - rds work function
+ */
+static void si470x_work(struct work_struct *work)
+{
+ struct si470x_device *radio = container_of(work, struct si470x_device,
+ work);
+
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ return;
+
+ si470x_rds(radio);
+ mod_timer(&radio->timer, jiffies + msecs_to_jiffies(rds_poll_time));
+}
+
+
+
+/**************************************************************************
+ * File Operations Interface
+ **************************************************************************/
+
+/*
+ * si470x_fops_read - read RDS data
+ */
+static ssize_t si470x_fops_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
+ unsigned int block_count = 0;
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
+ si470x_rds_on(radio);
+ schedule_work(&radio->work);
+ }
+
+ /* block if no new data available */
+ while (radio->wr_index == radio->rd_index) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+ interruptible_sleep_on(&radio->read_queue);
+ }
+
+ /* calculate block count from byte count */
+ count /= 3;
+
+ /* copy RDS block out of internal buffer and to user buffer */
+ if (spin_trylock(&radio->lock)) {
+ while (block_count < count) {
+ if (radio->rd_index == radio->wr_index)
+ break;
+
+ /* always transfer rds complete blocks */
+ if (copy_to_user(buf,
+ &radio->buffer[radio->rd_index], 3))
+ /* retval = -EFAULT; */
+ break;
+
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+
+ /* increment counters */
+ block_count++;
+ buf += 3;
+ retval += 3;
+ }
+
+ spin_unlock(&radio->lock);
+ }
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops_poll - poll RDS data
+ */
+static unsigned int si470x_fops_poll(struct file *file,
+ struct poll_table_struct *pts)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
+ si470x_rds_on(radio);
+ schedule_work(&radio->work);
+ }
+
+ poll_wait(file, &radio->read_queue, pts);
+
+ if (radio->rd_index != radio->wr_index)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+
+/*
+ * si470x_fops_open - file open
+ */
+static int si470x_fops_open(struct inode *inode, struct file *file)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ radio->users++;
+ if (radio->users == 1)
+ return si470x_start(radio);
+
+ return 0;
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static int si470x_fops_release(struct inode *inode, struct file *file)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (!radio)
+ return -ENODEV;
+
+ radio->users--;
+ if (radio->users == 0) {
+ /* stop rds reception */
+ del_timer_sync(&radio->timer);
+ flush_scheduled_work();
+
+ /* cancel read processes */
+ wake_up_interruptible(&radio->read_queue);
+
+ return si470x_stop(radio);
+ }
+
+ return 0;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+static const struct file_operations si470x_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = si470x_fops_read,
+ .poll = si470x_fops_poll,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .open = si470x_fops_open,
+ .release = si470x_fops_release,
+};
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_v4l2_queryctrl - query control
+ */
+static struct v4l2_queryctrl si470x_v4l2_queryctrl[] = {
+/* HINT: the disabled controls are only here to satify kradio and such apps */
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 15,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+};
+
+
+/*
+ * si470x_vidioc_querycap - query device capabilities
+ */
+static int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+ sprintf(capability->bus_info, "USB");
+ capability->version = DRIVER_VERSION;
+ capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_g_input - get input
+ */
+static int si470x_vidioc_g_input(struct file *filp, void *priv,
+ unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_input - set input
+ */
+static int si470x_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_queryctrl - enumerate control items
+ */
+static int si470x_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) {
+ if (qc->id && qc->id == si470x_v4l2_queryctrl[i].id) {
+ memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+
+/*
+ * si470x_vidioc_g_ctrl - get the value of a control
+ */
+static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = radio->registers[SYSCONFIG2] &
+ SYSCONFIG2_VOLUME;
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = ((radio->registers[POWERCFG] &
+ POWERCFG_DMUTE) == 0) ? 1 : 0;
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_ctrl - set the value of a control
+ */
+static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
+ radio->registers[SYSCONFIG2] |= ctrl->value;
+ return si470x_set_register(radio, SYSCONFIG2);
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value == 1)
+ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+ else
+ radio->registers[POWERCFG] |= POWERCFG_DMUTE;
+ return si470x_set_register(radio, POWERCFG);
+ }
+
+ return -EINVAL;
+}
+
+
+/*
+ * si470x_vidioc_g_audio - get audio attributes
+ */
+static int si470x_vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index > 1)
+ return -EINVAL;
+
+ strcpy(audio->name, "Radio");
+ audio->capability = V4L2_AUDCAP_STEREO;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_audio - set audio attributes
+ */
+static int si470x_vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_g_tuner - get tuner attributes
+ */
+static int si470x_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ int retval;
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ /* read status rssi */
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ return retval;
+
+ strcpy(tuner->name, "FM");
+ tuner->type = V4L2_TUNER_RADIO;
+ switch (band) {
+ /* 0: 87.5 - 108 MHz (USA, Europe, default) */
+ default:
+ tuner->rangelow = 87.5 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
+ break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ case 1 :
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
+ break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2 :
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 90 * FREQ_MUL;
+ break;
+ };
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ tuner->capability = V4L2_TUNER_CAP_LOW;
+
+ /* Stereo indicator == Stereo (instead of Mono) */
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1)
+ tuner->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ tuner->audmode = V4L2_TUNER_MODE_MONO;
+
+ /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
+ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI)
+ * 0x0101;
+
+ /* automatic frequency control: -1: freq to low, 1 freq to high */
+ tuner->afc = 0;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_tuner - set tuner attributes
+ */
+static int si470x_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ if (tuner->audmode == V4L2_TUNER_MODE_MONO)
+ radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
+ else
+ radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
+
+ return si470x_set_register(radio, POWERCFG);
+}
+
+
+/*
+ * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
+ */
+static int si470x_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ freq->type = V4L2_TUNER_RADIO;
+ freq->frequency = si470x_get_freq(radio);
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
+ */
+static int si470x_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (freq->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ return si470x_set_freq(radio, freq->frequency);
+}
+
+
+/*
+ * si470x_viddev_tamples - video device interface
+ */
+static struct video_device si470x_viddev_template = {
+ .fops = &si470x_fops,
+ .name = DRIVER_NAME,
+ .type = VID_TYPE_TUNER,
+ .release = video_device_release,
+ .vidioc_querycap = si470x_vidioc_querycap,
+ .vidioc_g_input = si470x_vidioc_g_input,
+ .vidioc_s_input = si470x_vidioc_s_input,
+ .vidioc_queryctrl = si470x_vidioc_queryctrl,
+ .vidioc_g_ctrl = si470x_vidioc_g_ctrl,
+ .vidioc_s_ctrl = si470x_vidioc_s_ctrl,
+ .vidioc_g_audio = si470x_vidioc_g_audio,
+ .vidioc_s_audio = si470x_vidioc_s_audio,
+ .vidioc_g_tuner = si470x_vidioc_g_tuner,
+ .vidioc_s_tuner = si470x_vidioc_s_tuner,
+ .vidioc_g_frequency = si470x_vidioc_g_frequency,
+ .vidioc_s_frequency = si470x_vidioc_s_frequency,
+ .owner = THIS_MODULE,
+};
+
+
+
+/**************************************************************************
+ * USB Interface
+ **************************************************************************/
+
+/*
+ * si470x_usb_driver_probe - probe for the device
+ */
+static int si470x_usb_driver_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct si470x_device *radio;
+
+ /* memory and interface allocations */
+ radio = kmalloc(sizeof(struct si470x_device), GFP_KERNEL);
+ if (!radio)
+ return -ENOMEM;
+ radio->videodev = video_device_alloc();
+ if (!radio->videodev) {
+ kfree(radio);
+ return -ENOMEM;
+ }
+ memcpy(radio->videodev, &si470x_viddev_template,
+ sizeof(si470x_viddev_template));
+ radio->users = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ video_set_drvdata(radio->videodev, radio);
+ if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+ printk(KERN_WARNING DRIVER_NAME
+ ": Could not register video device\n");
+ video_device_release(radio->videodev);
+ kfree(radio);
+ return -EIO;
+ }
+ usb_set_intfdata(intf, radio);
+
+ /* show some infos about the specific device */
+ if (si470x_get_all_registers(radio) < 0) {
+ video_device_release(radio->videodev);
+ kfree(radio);
+ return -EIO;
+ }
+ printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4x ChipID=0x%4.4x\n",
+ radio->registers[DEVICEID], radio->registers[CHIPID]);
+
+ /* check if firmware is current */
+ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE)
+ < RADIO_SW_VERSION_CURRENT)
+ printk(KERN_WARNING DRIVER_NAME
+ ": This driver is known to work with chip version %d, "
+ "but the device has firmware %d.\n"
+ DRIVER_NAME
+ "If you have some trouble using this driver, please "
+ "report to V4L ML at video4linux-list@redhat.com\n",
+ radio->registers[CHIPID] & CHIPID_FIRMWARE,
+ RADIO_SW_VERSION_CURRENT);
+
+ /* set initial frequency */
+ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
+ /* rds initialization */
+ radio->buf_size = rds_buf * 3;
+ radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
+ if (!radio->buffer) {
+ video_device_release(radio->videodev);
+ kfree(radio);
+ return -ENOMEM;
+ }
+ radio->wr_index = 0;
+ radio->rd_index = 0;
+ init_waitqueue_head(&radio->read_queue);
+
+ /* prepare polling via eventd */
+ INIT_WORK(&radio->work, si470x_work);
+ init_timer(&radio->timer);
+ radio->timer.function = si470x_timer;
+ radio->timer.data = (unsigned long) radio;
+
+ return 0;
+}
+
+
+/*
+ * si470x_usb_driver_disconnect - disconnect the device
+ */
+static void si470x_usb_driver_disconnect(struct usb_interface *intf)
+{
+ struct si470x_device *radio = usb_get_intfdata(intf);
+
+ del_timer_sync(&radio->timer);
+ flush_scheduled_work();
+
+ usb_set_intfdata(intf, NULL);
+ if (radio) {
+ video_unregister_device(radio->videodev);
+ kfree(radio->buffer);
+ kfree(radio);
+ }
+}
+
+
+/*
+ * si470x_usb_driver - usb driver interface
+ */
+static struct usb_driver si470x_usb_driver = {
+ .name = DRIVER_NAME,
+ .probe = si470x_usb_driver_probe,
+ .disconnect = si470x_usb_driver_disconnect,
+ .id_table = si470x_usb_driver_id_table,
+};
+
+
+
+/**************************************************************************
+ * Module Interface
+ **************************************************************************/
+
+/*
+ * si470x_module_init - module init
+ */
+static int __init si470x_module_init(void)
+{
+ printk(KERN_INFO DRIVER_DESC "\n");
+ return usb_register(&si470x_usb_driver);
+}
+
+
+/*
+ * si470x_module_exit - module exit
+ */
+static void __exit si470x_module_exit(void)
+{
+ usb_deregister(&si470x_usb_driver);
+}
+
+
+module_init(si470x_module_init);
+module_exit(si470x_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION("1.0.4");
diff --git a/trunk/drivers/media/video/Kconfig b/trunk/drivers/media/video/Kconfig
index c9f14bfc8544..a2e8987a6195 100644
--- a/trunk/drivers/media/video/Kconfig
+++ b/trunk/drivers/media/video/Kconfig
@@ -45,7 +45,7 @@ comment "Audio decoders"
config VIDEO_TVAUDIO
tristate "Simple audio decoder chips"
- depends on VIDEO_V4L1 && I2C
+ depends on VIDEO_V4L2 && I2C
---help---
Support for several audio decoder chips found on some bt8xx boards:
Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300,
@@ -57,7 +57,7 @@ config VIDEO_TVAUDIO
config VIDEO_TDA7432
tristate "Philips TDA7432 audio processor"
- depends on VIDEO_V4L1 && I2C
+ depends on VIDEO_V4L2 && I2C
---help---
Support for tda7432 audio decoder chip found on some bt8xx boards.
@@ -75,7 +75,7 @@ config VIDEO_TDA9840
config VIDEO_TDA9875
tristate "Philips TDA9875 audio processor"
- depends on VIDEO_V4L1 && I2C
+ depends on VIDEO_V4L2 && I2C
---help---
Support for tda9875 audio decoder chip found on some bt8xx boards.
@@ -109,9 +109,19 @@ config VIDEO_MSP3400
To compile this driver as a module, choose M here: the
module will be called msp3400.
+config VIDEO_CS5345
+ tristate "Cirrus Logic CS5345 audio ADC"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Cirrus Logic CS5345 24-bit, 192 kHz
+ stereo A/D converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs5345.
+
config VIDEO_CS53L32A
tristate "Cirrus Logic CS53L32A audio ADC"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Cirrus Logic CS53L32A low voltage
stereo A/D converter.
@@ -119,6 +129,15 @@ config VIDEO_CS53L32A
To compile this driver as a module, choose M here: the
module will be called cs53l32a.
+config VIDEO_M52790
+ tristate "Mitsubishi M52790 A/V switch"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Mitsubishi M52790 A/V switch.
+
+ To compile this driver as a module, choose M here: the
+ module will be called m52790.
+
config VIDEO_TLV320AIC23B
tristate "Texas Instruments TLV320AIC23B audio codec"
depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
@@ -130,7 +149,7 @@ config VIDEO_TLV320AIC23B
config VIDEO_WM8775
tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Wolfson Microelectronics WM8775 high
performance stereo A/D Converter with a 4 channel input mixer.
@@ -140,7 +159,7 @@ config VIDEO_WM8775
config VIDEO_WM8739
tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Wolfson Microelectronics WM8739
stereo A/D Converter.
@@ -244,7 +263,7 @@ config VIDEO_SAA7114
config VIDEO_SAA711X
tristate "Philips SAA7113/4/5 video decoders"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7113/4/5 video decoders.
@@ -300,7 +319,7 @@ comment "Video encoders"
config VIDEO_SAA7127
tristate "Philips SAA7127/9 digital video encoders"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7127/9 digital video encoders.
@@ -338,7 +357,7 @@ comment "Video improvement chips"
config VIDEO_UPD64031A
tristate "NEC Electronics uPD64031A Ghost Reduction"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the NEC Electronics uPD64031A Ghost Reduction
video chip. It is most often found in NTSC TV cards made for
@@ -350,7 +369,7 @@ config VIDEO_UPD64031A
config VIDEO_UPD64083
tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the NEC Electronics uPD64083 3-Dimensional Y/C
separation video chip. It is used to improve the quality of
@@ -802,6 +821,19 @@ config USB_ZR364XX
To compile this driver as a module, choose M here: the
module will be called zr364xx.
+config USB_STKWEBCAM
+ tristate "USB Syntek DC1125 Camera support"
+ depends on VIDEO_V4L2 && EXPERIMENTAL
+ ---help---
+ Say Y here if you want to use this type of camera.
+ Supported devices are typically found in some Asus laptops,
+ with USB id 174f:a311 and 05e1:0501. Other Syntek cameras
+ may be supported by the stk11xx driver, from which this is
+ derived, see http://stk11xx.sourceforge.net
+
+ To compile this driver as a module, choose M here: the
+ module will be called stkwebcam.
+
endif # V4L_USB_DRIVERS
endif # VIDEO_CAPTURE_DRIVERS
diff --git a/trunk/drivers/media/video/Makefile b/trunk/drivers/media/video/Makefile
index b5a064163e03..28ddd146c1c5 100644
--- a/trunk/drivers/media/video/Makefile
+++ b/trunk/drivers/media/video/Makefile
@@ -4,10 +4,12 @@
zr36067-objs := zoran_procfs.o zoran_device.o \
zoran_driver.o zoran_card.o
-tuner-objs := tuner-core.o tuner-types.o tda9887.o
+tuner-objs := tuner-core.o tuner-types.o
msp3400-objs := msp3400-driver.o msp3400-kthreads.o
+stkwebcam-objs := stk-webcam.o stk-sensor.o
+
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o \
v4l2-int-device.o
@@ -66,7 +68,9 @@ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
+obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
+obj-$(CONFIG_VIDEO_M52790) += m52790.o
obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o
obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
@@ -81,11 +85,13 @@ obj-$(CONFIG_TUNER_3036) += tuner-3036.o
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
+obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o
obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o
obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o
obj-$(CONFIG_TUNER_TDA8290) += tda8290.o
obj-$(CONFIG_TUNER_TEA5767) += tea5767.o
obj-$(CONFIG_TUNER_TEA5761) += tea5761.o
+obj-$(CONFIG_TUNER_TDA9887) += tda9887.o
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
@@ -112,6 +118,7 @@ obj-$(CONFIG_USB_SE401) += se401.o
obj-$(CONFIG_USB_STV680) += stv680.o
obj-$(CONFIG_USB_W9968CF) += w9968cf.o
obj-$(CONFIG_USB_ZR364XX) += zr364xx.o
+obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o
obj-$(CONFIG_USB_SN9C102) += sn9c102/
obj-$(CONFIG_USB_ET61X251) += et61x251/
@@ -129,3 +136,4 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/trunk/drivers/media/video/bt8xx/Kconfig b/trunk/drivers/media/video/bt8xx/Kconfig
index 2ca162b390a2..cfc822bb502a 100644
--- a/trunk/drivers/media/video/bt8xx/Kconfig
+++ b/trunk/drivers/media/video/bt8xx/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_BT848
tristate "BT848 Video For Linux"
- depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L1
+ depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 && INPUT
select I2C_ALGOBIT
select FW_LOADER
select VIDEO_BTCX
diff --git a/trunk/drivers/media/video/bt8xx/Makefile b/trunk/drivers/media/video/bt8xx/Makefile
index a096a03418aa..924d216d9570 100644
--- a/trunk/drivers/media/video/bt8xx/Makefile
+++ b/trunk/drivers/media/video/bt8xx/Makefile
@@ -4,7 +4,7 @@
bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \
- bttv-input.o
+ bttv-input.o bttv-audio-hook.o
obj-$(CONFIG_VIDEO_BT848) += bttv.o
diff --git a/trunk/drivers/media/video/bt8xx/bttv-audio-hook.c b/trunk/drivers/media/video/bt8xx/bttv-audio-hook.c
new file mode 100644
index 000000000000..2364d16586b3
--- /dev/null
+++ b/trunk/drivers/media/video/bt8xx/bttv-audio-hook.c
@@ -0,0 +1,382 @@
+/*
+ * Handlers for board audio hooks, splitted from bttv-cards
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License
+ */
+
+#include "bttv-audio-hook.h"
+
+#include
+
+/* ----------------------------------------------------------------------- */
+/* winview */
+
+void winview_volume(struct bttv *btv, __u16 volume)
+{
+ /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */
+ int bits_out, loops, vol, data;
+
+ /* 32 levels logarithmic */
+ vol = 32 - ((volume>>11));
+ /* units */
+ bits_out = (PT2254_DBS_IN_2>>(vol%5));
+ /* tens */
+ bits_out |= (PT2254_DBS_IN_10>>(vol/5));
+ bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL;
+ data = gpio_read();
+ data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA|
+ WINVIEW_PT2254_STROBE);
+ for (loops = 17; loops >= 0 ; loops--) {
+ if (bits_out & (1<audmode & V4L2_TUNER_MODE_LANG1)
+ con = 0x000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x300;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x200;
+/* if (t->audmode & V4L2_TUNER_MODE_MONO)
+ * con = 0x100; */
+ gpio_bits(0x300, con);
+ } else {
+ t->audmode = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val, con;
+
+ if (btv->radio_user)
+ return;
+
+ val = gpio_read();
+ if (set) {
+ con = 0x000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG1) {
+ /* LANG1 + LANG2 */
+ con = 0x100;
+ }
+ else {
+ /* LANG2 */
+ con = 0x300;
+ }
+ }
+ if (con != (val & 0x300)) {
+ gpio_bits(0x300, con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"gvbctv5pci");
+ }
+ } else {
+ switch (val & 0x70) {
+ case 0x10:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x30:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x50:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1;
+ break;
+ case 0x60:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ break;
+ case 0x70:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ default:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO |
+ V4L2_TUNER_SUB_STEREO |
+ V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ }
+ t->audmode = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Mario Medina Nussbaum
+ * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo,
+ * 0xdde enables mono and 0xccd enables sap
+ *
+ * Petr Vandrovec
+ * P.S.: At least mask in line above is wrong - GPIO pins 3,2 select
+ * input/output sound connection, so both must be set for output mode.
+ *
+ * Looks like it's needed only for the "tvphone", the "tvphone 98"
+ * handles this with a tda9840
+ *
+ */
+
+void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x02;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ val = 0x01;
+ if (val) {
+ gpio_bits(0x03,val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"avermedia");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1;
+ return;
+ }
+}
+
+
+void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x01;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* STEREO */
+ val = 0x02;
+ btaor(val, ~0x03, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"avermedia");
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ return;
+ }
+}
+
+/* Lifetec 9415 handling */
+
+void lt9415_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (gpio_read() & 0x4000) {
+ t->audmode = V4L2_TUNER_MODE_MONO;
+ return;
+ }
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* A2 SAP */
+ val = 0x0080;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* A2 stereo */
+ val = 0x0880;
+ if ((t->audmode & V4L2_TUNER_MODE_LANG1) ||
+ (t->audmode & V4L2_TUNER_MODE_MONO))
+ val = 0;
+ gpio_bits(0x0880, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"lt9415");
+ } else {
+ /* autodetect doesn't work with this card :-( */
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ return;
+ }
+}
+
+/* TDA9821 on TerraTV+ Bt848, Bt878 */
+void terratv_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ gpio_inout(0x180000,0x180000);
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x080000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x180000;
+ gpio_bits(0x180000, con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"terratv");
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+
+void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned long val = 0;
+
+ if (set) {
+ /*btor (0xc32000, BT848_GPIO_OUT_EN);*/
+ if (t->audmode & V4L2_TUNER_MODE_MONO) /* Mono */
+ val = 0x420000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG1) /* Mono */
+ val = 0x420000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x410000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* Stereo */
+ val = 0x020000;
+ if (val) {
+ gpio_bits(0x430000, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"winfast2000");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Dariusz Kowalewski
+ * sound control for Prolink PV-BT878P+9B (PixelView PlayTV Pro FM+NICAM
+ * revision 9B has on-board TDA9874A sound decoder).
+ *
+ * Note: There are card variants without tda9874a. Forcing the "stereo sound route"
+ * will mute this cards.
+ */
+void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val = 0;
+
+ if (btv->radio_user)
+ return;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO) {
+ val = 0x01;
+ }
+ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2))
+ || (t->audmode & V4L2_TUNER_MODE_STEREO)) {
+ val = 0x02;
+ }
+ if (val) {
+ gpio_bits(0x03,val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pvbt878p9b");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Dariusz Kowalewski
+ * sound control for FlyVideo 2000S (with tda9874 decoder)
+ * based on pvbt878p9b_audio() - this is not tested, please fix!!!
+ */
+void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val = 0xffff;
+
+ if (btv->radio_user)
+ return;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO) {
+ val = 0x0000;
+ }
+ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2))
+ || (t->audmode & V4L2_TUNER_MODE_STEREO)) {
+ val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */
+ }
+ if (val != 0xffff) {
+ gpio_bits(0x1800, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"fv2000s");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * sound control for Canopus WinDVR PCI
+ * Masaki Suzuki
+ */
+void windvr_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned long val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO)
+ val = 0x040000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ val = 0;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ val = 0x100000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ val = 0;
+ if (val) {
+ gpio_bits(0x140000, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"windvr");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * sound control for AD-TVK503
+ * Hiroshi Takekawa
+ */
+void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0xffffff;
+
+ /* btaor(0x1e0000, ~0x1e0000, BT848_GPIO_OUT_EN); */
+
+ if (set) {
+ /* btor(***, BT848_GPIO_OUT_EN); */
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ con = 0x00000000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x00180000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x00000000;
+ if (t->audmode & V4L2_TUNER_MODE_MONO)
+ con = 0x00060000;
+ if (con != 0xffffff) {
+ gpio_bits(0x1e0000,con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv, "adtvk503");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
diff --git a/trunk/drivers/media/video/bt8xx/bttv-audio-hook.h b/trunk/drivers/media/video/bt8xx/bttv-audio-hook.h
new file mode 100644
index 000000000000..159d07adeff8
--- /dev/null
+++ b/trunk/drivers/media/video/bt8xx/bttv-audio-hook.h
@@ -0,0 +1,23 @@
+/*
+ * Handlers for board audio hooks, splitted from bttv-cards
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License
+ */
+
+#include "bttvp.h"
+
+void winview_volume (struct bttv *btv, __u16 volume);
+
+void lt9415_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void terratv_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void windvr_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+
diff --git a/trunk/drivers/media/video/bt8xx/bttv-cards.c b/trunk/drivers/media/video/bt8xx/bttv-cards.c
index 585d1ef95afd..63a47cd4c161 100644
--- a/trunk/drivers/media/video/bt8xx/bttv-cards.c
+++ b/trunk/drivers/media/video/bt8xx/bttv-cards.c
@@ -39,6 +39,7 @@
#include "bttvp.h"
#include
#include
+#include "bttv-audio-hook.h"
/* fwd decl */
static void boot_msp34xx(struct bttv *btv, int pin);
@@ -50,20 +51,6 @@ static void modtec_eeprom(struct bttv *btv);
static void init_PXC200(struct bttv *btv);
static void init_RTV24(struct bttv *btv);
-static void winview_audio(struct bttv *btv, struct video_audio *v, int set);
-static void lt9415_audio(struct bttv *btv, struct video_audio *v, int set);
-static void avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v,
- int set);
-static void avermedia_tv_stereo_audio(struct bttv *btv, struct video_audio *v,
- int set);
-static void terratv_audio(struct bttv *btv, struct video_audio *v, int set);
-static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set);
-static void gvbctv5pci_audio(struct bttv *btv, struct video_audio *v, int set);
-static void winfast2000_audio(struct bttv *btv, struct video_audio *v, int set);
-static void pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set);
-static void fv2000s_audio(struct bttv *btv, struct video_audio *v, int set);
-static void windvr_audio(struct bttv *btv, struct video_audio *v, int set);
-static void adtvk503_audio(struct bttv *btv, struct video_audio *v, int set);
static void rv605_muxsel(struct bttv *btv, unsigned int input);
static void eagle_muxsel(struct bttv *btv, unsigned int input);
static void xguard_muxsel(struct bttv *btv, unsigned int input);
@@ -427,7 +414,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = avermedia_tvphone_audio,
+ .audio_mode_gpio= avermedia_tvphone_audio,
.has_remote = 1,
},
[BTTV_BOARD_MATRIX_VISION] = {
@@ -539,7 +526,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = avermedia_tv_stereo_audio,
+ .audio_mode_gpio= avermedia_tv_stereo_audio,
.no_gpioirq = 1,
},
[BTTV_BOARD_VHX] = {
@@ -604,7 +591,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = winview_audio,
+ .volume_gpio = winview_volume,
.has_radio = 1,
},
[BTTV_BOARD_AVEC_INTERCAP] = {
@@ -728,7 +715,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = terratv_audio,
+ .audio_mode_gpio= terratv_audio,
},
[BTTV_BOARD_HAUPPAUG_WCAM] = {
.name = "Hauppauge WinCam newer (bt878)",
@@ -776,7 +763,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = terratv_audio,
+ .audio_mode_gpio= terratv_audio,
/* GPIO wiring:
External 20 pin connector (for Active Radio Upgrade board)
gpio00: i2c-sda
@@ -915,7 +902,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = winfast2000_audio,
+ .audio_mode_gpio= winfast2000_audio,
.has_remote = 1,
},
[BTTV_BOARD_CHRONOS_VS2] = {
@@ -1035,7 +1022,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.has_radio = 1,
- .audio_hook = avermedia_tvphone_audio,
+ .audio_mode_gpio= avermedia_tvphone_audio,
},
[BTTV_BOARD_PV951] = {
.name = "ProVideo PV951", /* pic16c54 */
@@ -1167,7 +1154,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_ALPS_TSHC6_NTSC,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = gvbctv3pci_audio,
+ .audio_mode_gpio= gvbctv3pci_audio,
},
[BTTV_BOARD_PXELVWPLTVPAK] = {
.name = "Prolink PV-BT878P+4E / PixelView PlayTV PAK / Lenco MXTV-9578 CP",
@@ -1472,7 +1459,7 @@ struct tvcard bttv_tvcards[] = {
/* -dk-???: set mute=0x1800 for tda9874h daughterboard */
.gpiomux = { 0x0000,0x0800,0x1000,0x1000 },
.gpiomute = 0x1800,
- .audio_hook = fv2000s_audio,
+ .audio_mode_gpio= fv2000s_audio,
.no_msp34xx = 1,
.no_tda9875 = 1,
.needs_tvaudio = 1,
@@ -1513,7 +1500,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_SHARP_2U5JF5540_NTSC,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = gvbctv3pci_audio,
+ .audio_mode_gpio= gvbctv3pci_audio,
},
/* ---- card 0x44 ---------------------------------- */
@@ -1632,7 +1619,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = pvbt878p9b_audio, /* Note: not all cards have stereo */
+ .audio_mode_gpio= pvbt878p9b_audio, /* Note: not all cards have stereo */
.has_radio = 1, /* Note: not all cards have radio */
.has_remote = 1,
/* GPIO wiring:
@@ -1710,7 +1697,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_NTSC,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = windvr_audio,
+ .audio_mode_gpio= windvr_audio,
},
[BTTV_BOARD_GRANDTEC_MULTI] = {
.name = "GrandTec Multi Capture Card (Bt878)",
@@ -1807,7 +1794,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_NTSC_M,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = gvbctv5pci_audio,
+ .audio_mode_gpio= gvbctv5pci_audio,
.has_radio = 1,
},
[BTTV_BOARD_OSPREY1x0] = {
@@ -2106,7 +2093,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_NTSC,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = adtvk503_audio,
+ .audio_mode_gpio= adtvk503_audio,
},
/* ---- card 0x64 ---------------------------------- */
@@ -3173,8 +3160,8 @@ static void flyvideo_gpio(struct bttv *btv)
/* LR90 Audio Routing is done by 2 hef4052, so Audio_Mask has 4 bits: 0x001c80
* LR26/LR50 only has 1 hef4052, Audio_Mask 0x000c00
* Audio options: from tuner, from tda9821/tda9821(mono,stereo,sap), from tda9874, ext., mute */
- if(has_tda9820_tda9821) btv->audio_hook = lt9415_audio;
- /* todo: if(has_tda9874) btv->audio_hook = fv2000s_audio; */
+ if(has_tda9820_tda9821) btv->audio_mode_gpio = lt9415_audio;
+ /* todo: if(has_tda9874) btv->audio_mode_gpio = fv2000s_audio; */
}
static int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1,
@@ -3574,8 +3561,12 @@ void __devinit bttv_init_card2(struct bttv *btv)
}
if (btv->tda9887_conf) {
- bttv_call_i2c_clients(btv, TDA9887_SET_CONFIG,
- &btv->tda9887_conf);
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &btv->tda9887_conf;
+
+ bttv_call_i2c_clients(btv, TUNER_SET_CONFIG, &tda9887_cfg);
}
btv->svhs = bttv_tvcards[btv->c.type].svhs;
@@ -3590,8 +3581,10 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->has_remote=1;
if (!bttv_tvcards[btv->c.type].no_gpioirq)
btv->gpioirq=1;
- if (bttv_tvcards[btv->c.type].audio_hook)
- btv->audio_hook=bttv_tvcards[btv->c.type].audio_hook;
+ if (bttv_tvcards[btv->c.type].volume_gpio)
+ btv->volume_gpio=bttv_tvcards[btv->c.type].volume_gpio;
+ if (bttv_tvcards[btv->c.type].audio_mode_gpio)
+ btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio;
if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) {
/* detect Bt832 chip for quartzsight digital camera */
@@ -3950,7 +3943,7 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
void bttv_tda9880_setnorm(struct bttv *btv, int norm)
{
/* fix up our card entry */
- if(norm==VIDEO_MODE_NTSC) {
+ if(norm==V4L2_STD_NTSC) {
bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff;
bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x957fff;
bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff;
@@ -4319,387 +4312,6 @@ void tea5757_set_freq(struct bttv *btv, unsigned short freq)
tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */
}
-
-/* ----------------------------------------------------------------------- */
-/* winview */
-
-static void winview_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */
- int bits_out, loops, vol, data;
-
- if (!set) {
- /* Fixed by Leandro Lucarella flags |= VIDEO_AUDIO_VOLUME;
- return;
- }
-
- /* 32 levels logarithmic */
- vol = 32 - ((v->volume>>11));
- /* units */
- bits_out = (PT2254_DBS_IN_2>>(vol%5));
- /* tens */
- bits_out |= (PT2254_DBS_IN_10>>(vol/5));
- bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL;
- data = gpio_read();
- data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA|
- WINVIEW_PT2254_STROBE);
- for (loops = 17; loops >= 0 ; loops--) {
- if (bits_out & (1<mode & VIDEO_SOUND_LANG1)
- con = 0x000;
- if (v->mode & VIDEO_SOUND_LANG2)
- con = 0x300;
- if (v->mode & VIDEO_SOUND_STEREO)
- con = 0x200;
-/* if (v->mode & VIDEO_SOUND_MONO)
- * con = 0x100; */
- gpio_bits(0x300, con);
- } else {
- v->mode = VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-static void
-gvbctv5pci_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int val, con;
-
- if (btv->radio_user)
- return;
-
- val = gpio_read();
- if (set) {
- con = 0x000;
- if (v->mode & VIDEO_SOUND_LANG2) {
- if (v->mode & VIDEO_SOUND_LANG1) {
- /* LANG1 + LANG2 */
- con = 0x100;
- }
- else {
- /* LANG2 */
- con = 0x300;
- }
- }
- if (con != (val & 0x300)) {
- gpio_bits(0x300, con);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"gvbctv5pci");
- }
- } else {
- switch (val & 0x70) {
- case 0x10:
- v->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- break;
- case 0x30:
- v->mode = VIDEO_SOUND_LANG2;
- break;
- case 0x50:
- v->mode = VIDEO_SOUND_LANG1;
- break;
- case 0x60:
- v->mode = VIDEO_SOUND_STEREO;
- break;
- case 0x70:
- v->mode = VIDEO_SOUND_MONO;
- break;
- default:
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
- }
-}
-
-/*
- * Mario Medina Nussbaum
- * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo,
- * 0xdde enables mono and 0xccd enables sap
- *
- * Petr Vandrovec
- * P.S.: At least mask in line above is wrong - GPIO pins 3,2 select
- * input/output sound connection, so both must be set for output mode.
- *
- * Looks like it's needed only for the "tvphone", the "tvphone 98"
- * handles this with a tda9840
- *
- */
-static void
-avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- int val = 0;
-
- if (set) {
- if (v->mode & VIDEO_SOUND_LANG2) /* SAP */
- val = 0x02;
- if (v->mode & VIDEO_SOUND_STEREO)
- val = 0x01;
- if (val) {
- gpio_bits(0x03,val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"avermedia");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1;
- return;
- }
-}
-
-static void
-avermedia_tv_stereo_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- int val = 0;
-
- if (set) {
- if (v->mode & VIDEO_SOUND_LANG2) /* SAP */
- val = 0x01;
- if (v->mode & VIDEO_SOUND_STEREO) /* STEREO */
- val = 0x02;
- btaor(val, ~0x03, BT848_GPIO_DATA);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"avermedia");
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- return;
- }
-}
-
-/* Lifetec 9415 handling */
-static void
-lt9415_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- int val = 0;
-
- if (gpio_read() & 0x4000) {
- v->mode = VIDEO_SOUND_MONO;
- return;
- }
-
- if (set) {
- if (v->mode & VIDEO_SOUND_LANG2) /* A2 SAP */
- val = 0x0080;
- if (v->mode & VIDEO_SOUND_STEREO) /* A2 stereo */
- val = 0x0880;
- if ((v->mode & VIDEO_SOUND_LANG1) ||
- (v->mode & VIDEO_SOUND_MONO))
- val = 0;
- gpio_bits(0x0880, val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"lt9415");
- } else {
- /* autodetect doesn't work with this card :-( */
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- return;
- }
-}
-
-/* TDA9821 on TerraTV+ Bt848, Bt878 */
-static void
-terratv_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int con = 0;
-
- if (set) {
- gpio_inout(0x180000,0x180000);
- if (v->mode & VIDEO_SOUND_LANG2)
- con = 0x080000;
- if (v->mode & VIDEO_SOUND_STEREO)
- con = 0x180000;
- gpio_bits(0x180000, con);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"terratv");
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-static void
-winfast2000_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned long val = 0;
-
- if (set) {
- /*btor (0xc32000, BT848_GPIO_OUT_EN);*/
- if (v->mode & VIDEO_SOUND_MONO) /* Mono */
- val = 0x420000;
- if (v->mode & VIDEO_SOUND_LANG1) /* Mono */
- val = 0x420000;
- if (v->mode & VIDEO_SOUND_LANG2) /* SAP */
- val = 0x410000;
- if (v->mode & VIDEO_SOUND_STEREO) /* Stereo */
- val = 0x020000;
- if (val) {
- gpio_bits(0x430000, val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"winfast2000");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-/*
- * Dariusz Kowalewski
- * sound control for Prolink PV-BT878P+9B (PixelView PlayTV Pro FM+NICAM
- * revision 9B has on-board TDA9874A sound decoder).
- *
- * Note: There are card variants without tda9874a. Forcing the "stereo sound route"
- * will mute this cards.
- */
-static void
-pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int val = 0;
-
- if (btv->radio_user)
- return;
-
- if (set) {
- if (v->mode & VIDEO_SOUND_MONO) {
- val = 0x01;
- }
- if ((v->mode & (VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2))
- || (v->mode & VIDEO_SOUND_STEREO)) {
- val = 0x02;
- }
- if (val) {
- gpio_bits(0x03,val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"pvbt878p9b");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-/*
- * Dariusz Kowalewski
- * sound control for FlyVideo 2000S (with tda9874 decoder)
- * based on pvbt878p9b_audio() - this is not tested, please fix!!!
- */
-static void
-fv2000s_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int val = 0xffff;
-
- if (btv->radio_user)
- return;
- if (set) {
- if (v->mode & VIDEO_SOUND_MONO) {
- val = 0x0000;
- }
- if ((v->mode & (VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2))
- || (v->mode & VIDEO_SOUND_STEREO)) {
- val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */
- }
- if (val != 0xffff) {
- gpio_bits(0x1800, val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"fv2000s");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-/*
- * sound control for Canopus WinDVR PCI
- * Masaki Suzuki
- */
-static void
-windvr_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned long val = 0;
-
- if (set) {
- if (v->mode & VIDEO_SOUND_MONO)
- val = 0x040000;
- if (v->mode & VIDEO_SOUND_LANG1)
- val = 0;
- if (v->mode & VIDEO_SOUND_LANG2)
- val = 0x100000;
- if (v->mode & VIDEO_SOUND_STEREO)
- val = 0;
- if (val) {
- gpio_bits(0x140000, val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"windvr");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-/*
- * sound control for AD-TVK503
- * Hiroshi Takekawa
- */
-static void
-adtvk503_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int con = 0xffffff;
-
- /* btaor(0x1e0000, ~0x1e0000, BT848_GPIO_OUT_EN); */
-
- if (set) {
- /* btor(***, BT848_GPIO_OUT_EN); */
- if (v->mode & VIDEO_SOUND_LANG1)
- con = 0x00000000;
- if (v->mode & VIDEO_SOUND_LANG2)
- con = 0x00180000;
- if (v->mode & VIDEO_SOUND_STEREO)
- con = 0x00000000;
- if (v->mode & VIDEO_SOUND_MONO)
- con = 0x00060000;
- if (con != 0xffffff) {
- gpio_bits(0x1e0000,con);
- if (bttv_gpio)
- bttv_gpio_tracking(btv, "adtvk503");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas]
*
* This is needed because rv605 don't use a normal multiplex, but a crosspoint
diff --git a/trunk/drivers/media/video/bt8xx/bttv-driver.c b/trunk/drivers/media/video/bt8xx/bttv-driver.c
index 581a3c955739..907dc62c1783 100644
--- a/trunk/drivers/media/video/bt8xx/bttv-driver.c
+++ b/trunk/drivers/media/video/bt8xx/bttv-driver.c
@@ -9,6 +9,12 @@
some v4l2 code lines are taken from Justin's bttv2 driver which is
(c) 2000 Justin Schoeman
+ V4L1 removal from:
+ (c) 2005-2006 Nickolay V. Shmyrev
+
+ Fixes to be fully V4L2 compliant by
+ (c) 2006 Mauro Carvalho Chehab
+
Cropping and overscan support
Copyright (C) 2005, 2006 Michael H. Schimek
Sponsored by OPQ Systems AB
@@ -157,7 +163,7 @@ MODULE_LICENSE("GPL");
static ssize_t show_card(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = to_video_device(cd);
+ struct video_device *vfd = container_of(cd, struct video_device, class_dev);
struct bttv *btv = dev_get_drvdata(vfd->dev);
return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
}
@@ -470,31 +476,27 @@ static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
/* ----------------------------------------------------------------------- */
/* bttv format list
packed pixel formats must come first */
-static const struct bttv_format bttv_formats[] = {
+static const struct bttv_format formats[] = {
{
.name = "8 bpp, gray",
- .palette = VIDEO_PALETTE_GREY,
.fourcc = V4L2_PIX_FMT_GREY,
.btformat = BT848_COLOR_FMT_Y8,
.depth = 8,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "8 bpp, dithered color",
- .palette = VIDEO_PALETTE_HI240,
.fourcc = V4L2_PIX_FMT_HI240,
.btformat = BT848_COLOR_FMT_RGB8,
.depth = 8,
.flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
},{
.name = "15 bpp RGB, le",
- .palette = VIDEO_PALETTE_RGB555,
.fourcc = V4L2_PIX_FMT_RGB555,
.btformat = BT848_COLOR_FMT_RGB15,
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "15 bpp RGB, be",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_RGB555X,
.btformat = BT848_COLOR_FMT_RGB15,
.btswap = 0x03, /* byteswap */
@@ -502,14 +504,12 @@ static const struct bttv_format bttv_formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "16 bpp RGB, le",
- .palette = VIDEO_PALETTE_RGB565,
.fourcc = V4L2_PIX_FMT_RGB565,
.btformat = BT848_COLOR_FMT_RGB16,
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "16 bpp RGB, be",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_RGB565X,
.btformat = BT848_COLOR_FMT_RGB16,
.btswap = 0x03, /* byteswap */
@@ -517,21 +517,18 @@ static const struct bttv_format bttv_formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "24 bpp RGB, le",
- .palette = VIDEO_PALETTE_RGB24,
.fourcc = V4L2_PIX_FMT_BGR24,
.btformat = BT848_COLOR_FMT_RGB24,
.depth = 24,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "32 bpp RGB, le",
- .palette = VIDEO_PALETTE_RGB32,
.fourcc = V4L2_PIX_FMT_BGR32,
.btformat = BT848_COLOR_FMT_RGB32,
.depth = 32,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "32 bpp RGB, be",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_RGB32,
.btformat = BT848_COLOR_FMT_RGB32,
.btswap = 0x0f, /* byte+word swap */
@@ -539,21 +536,18 @@ static const struct bttv_format bttv_formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "4:2:2, packed, YUYV",
- .palette = VIDEO_PALETTE_YUV422,
.fourcc = V4L2_PIX_FMT_YUYV,
.btformat = BT848_COLOR_FMT_YUY2,
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "4:2:2, packed, YUYV",
- .palette = VIDEO_PALETTE_YUYV,
.fourcc = V4L2_PIX_FMT_YUYV,
.btformat = BT848_COLOR_FMT_YUY2,
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "4:2:2, packed, UYVY",
- .palette = VIDEO_PALETTE_UYVY,
.fourcc = V4L2_PIX_FMT_UYVY,
.btformat = BT848_COLOR_FMT_YUY2,
.btswap = 0x03, /* byteswap */
@@ -561,7 +555,6 @@ static const struct bttv_format bttv_formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "4:2:2, planar, Y-Cb-Cr",
- .palette = VIDEO_PALETTE_YUV422P,
.fourcc = V4L2_PIX_FMT_YUV422P,
.btformat = BT848_COLOR_FMT_YCrCb422,
.depth = 16,
@@ -570,7 +563,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 0,
},{
.name = "4:2:0, planar, Y-Cb-Cr",
- .palette = VIDEO_PALETTE_YUV420P,
.fourcc = V4L2_PIX_FMT_YUV420,
.btformat = BT848_COLOR_FMT_YCrCb422,
.depth = 12,
@@ -579,7 +571,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 1,
},{
.name = "4:2:0, planar, Y-Cr-Cb",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_YVU420,
.btformat = BT848_COLOR_FMT_YCrCb422,
.depth = 12,
@@ -588,7 +579,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 1,
},{
.name = "4:1:1, planar, Y-Cb-Cr",
- .palette = VIDEO_PALETTE_YUV411P,
.fourcc = V4L2_PIX_FMT_YUV411P,
.btformat = BT848_COLOR_FMT_YCrCb411,
.depth = 12,
@@ -597,7 +587,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 0,
},{
.name = "4:1:0, planar, Y-Cb-Cr",
- .palette = VIDEO_PALETTE_YUV410P,
.fourcc = V4L2_PIX_FMT_YUV410,
.btformat = BT848_COLOR_FMT_YCrCb411,
.depth = 9,
@@ -606,7 +595,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 2,
},{
.name = "4:1:0, planar, Y-Cr-Cb",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_YVU410,
.btformat = BT848_COLOR_FMT_YCrCb411,
.depth = 9,
@@ -615,14 +603,13 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 2,
},{
.name = "raw scanlines",
- .palette = VIDEO_PALETTE_RAW,
.fourcc = -1,
.btformat = BT848_COLOR_FMT_RAW,
.depth = 8,
.flags = FORMAT_FLAGS_RAW,
}
};
-static const unsigned int BTTV_FORMATS = ARRAY_SIZE(bttv_formats);
+static const unsigned int FORMATS = ARRAY_SIZE(formats);
/* ----------------------------------------------------------------------- */
@@ -798,7 +785,17 @@ static const struct v4l2_queryctrl bttv_ctls[] = {
};
-static const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls);
+
+static const struct v4l2_queryctrl *ctrl_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
+ if (bttv_ctls[i].id == id)
+ return bttv_ctls+i;
+
+ return NULL;
+}
/* ----------------------------------------------------------------------- */
/* resource management */
@@ -1254,16 +1251,6 @@ audio_input(struct bttv *btv, int input)
return audio_mux(btv, input, btv->mute);
}
-static void
-i2c_vidiocschan(struct bttv *btv)
-{
- v4l2_std_id std = bttv_tvnorms[btv->tvnorm].v4l2_id;
-
- bttv_call_i2c_clients(btv, VIDIOC_S_STD, &std);
- if (btv->c.type == BTTV_BOARD_VOODOOTV_FM || btv->c.type == BTTV_BOARD_VOODOOTV_200)
- bttv_tda9880_setnorm(btv,btv->tvnorm);
-}
-
static void
bttv_crop_calc_limits(struct bttv_crop *c)
{
@@ -1298,6 +1285,7 @@ static int
set_tvnorm(struct bttv *btv, unsigned int norm)
{
const struct bttv_tvnorm *tvnorm;
+ v4l2_std_id id;
if (norm < 0 || norm >= BTTV_TVNORMS)
return -EINVAL;
@@ -1334,6 +1322,9 @@ set_tvnorm(struct bttv *btv, unsigned int norm)
bttv_tda9880_setnorm(btv,norm);
break;
}
+ id = tvnorm->v4l2_id;
+ bttv_call_i2c_clients(btv, VIDIOC_S_STD, &id);
+
return 0;
}
@@ -1359,7 +1350,6 @@ set_input(struct bttv *btv, unsigned int input, unsigned int norm)
audio_input(btv,(input == bttv_tvcards[btv->c.type].tuner ?
TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN));
set_tvnorm(btv, norm);
- i2c_vidiocschan(btv);
}
static void init_irqreg(struct bttv *btv)
@@ -1452,38 +1442,12 @@ static void bttv_reinit_bt848(struct bttv *btv)
set_input(btv, btv->input, btv->tvnorm);
}
-static int get_control(struct bttv *btv, struct v4l2_control *c)
+static int bttv_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
{
- struct video_audio va;
- int i;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- for (i = 0; i < BTTV_CTLS; i++)
- if (bttv_ctls[i].id == c->id)
- break;
- if (i == BTTV_CTLS)
- return -EINVAL;
- if (btv->audio_hook && i >= 4 && i <= 8) {
- memset(&va,0,sizeof(va));
- btv->audio_hook(btv,&va,0);
- switch (c->id) {
- case V4L2_CID_AUDIO_MUTE:
- c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- c->value = va.volume;
- break;
- case V4L2_CID_AUDIO_BALANCE:
- c->value = va.balance;
- break;
- case V4L2_CID_AUDIO_BASS:
- c->value = va.bass;
- break;
- case V4L2_CID_AUDIO_TREBLE:
- c->value = va.treble;
- break;
- }
- return 0;
- }
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
c->value = btv->bright;
@@ -1503,7 +1467,7 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
- bttv_call_i2c_clients(btv,VIDIOC_G_CTRL,c);
+ bttv_call_i2c_clients(btv, VIDIOC_G_CTRL, c);
break;
case V4L2_CID_PRIVATE_CHROMA_AGC:
@@ -1545,67 +1509,44 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
return 0;
}
-static int set_control(struct bttv *btv, struct v4l2_control *c)
+static int bttv_s_ctrl(struct file *file, void *f,
+ struct v4l2_control *c)
{
- struct video_audio va;
- int i,val;
+ int err;
+ int val;
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- for (i = 0; i < BTTV_CTLS; i++)
- if (bttv_ctls[i].id == c->id)
- break;
- if (i == BTTV_CTLS)
- return -EINVAL;
- if (btv->audio_hook && i >= 4 && i <= 8) {
- memset(&va,0,sizeof(va));
- btv->audio_hook(btv,&va,0);
- switch (c->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (c->value) {
- va.flags |= VIDEO_AUDIO_MUTE;
- audio_mute(btv, 1);
- } else {
- va.flags &= ~VIDEO_AUDIO_MUTE;
- audio_mute(btv, 0);
- }
- break;
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- case V4L2_CID_AUDIO_VOLUME:
- va.volume = c->value;
- break;
- case V4L2_CID_AUDIO_BALANCE:
- va.balance = c->value;
- break;
- case V4L2_CID_AUDIO_BASS:
- va.bass = c->value;
- break;
- case V4L2_CID_AUDIO_TREBLE:
- va.treble = c->value;
- break;
- }
- btv->audio_hook(btv,&va,1);
- return 0;
- }
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
- bt848_bright(btv,c->value);
+ bt848_bright(btv, c->value);
break;
case V4L2_CID_HUE:
- bt848_hue(btv,c->value);
+ bt848_hue(btv, c->value);
break;
case V4L2_CID_CONTRAST:
- bt848_contrast(btv,c->value);
+ bt848_contrast(btv, c->value);
break;
case V4L2_CID_SATURATION:
- bt848_sat(btv,c->value);
+ bt848_sat(btv, c->value);
break;
case V4L2_CID_AUDIO_MUTE:
audio_mute(btv, c->value);
/* fall through */
case V4L2_CID_AUDIO_VOLUME:
+ if (btv->volume_gpio)
+ btv->volume_gpio(btv, c->value);
+
+ bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c);
+ break;
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
- bttv_call_i2c_clients(btv,VIDIOC_S_CTRL,c);
+ bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c);
break;
case V4L2_CID_PRIVATE_CHROMA_AGC:
@@ -1632,8 +1573,9 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
break;
case V4L2_CID_PRIVATE_AGC_CRUSH:
btv->opt_adc_crush = c->value;
- btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
- BT848_ADC);
+ btwrite(BT848_ADC_RESERVED |
+ (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
break;
case V4L2_CID_PRIVATE_VCR_HACK:
btv->opt_vcr_hack = c->value;
@@ -1692,30 +1634,16 @@ static void bttv_field_count(struct bttv *btv)
}
}
-static const struct bttv_format*
-format_by_palette(int palette)
-{
- unsigned int i;
-
- for (i = 0; i < BTTV_FORMATS; i++) {
- if (-1 == bttv_formats[i].palette)
- continue;
- if (bttv_formats[i].palette == palette)
- return bttv_formats+i;
- }
- return NULL;
-}
-
static const struct bttv_format*
format_by_fourcc(int fourcc)
{
unsigned int i;
- for (i = 0; i < BTTV_FORMATS; i++) {
- if (-1 == bttv_formats[i].fourcc)
+ for (i = 0; i < FORMATS; i++) {
+ if (-1 == formats[i].fourcc)
continue;
- if (bttv_formats[i].fourcc == fourcc)
- return bttv_formats+i;
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
}
return NULL;
}
@@ -1733,7 +1661,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
dprintk("switch_overlay: enter [new=%p]\n",new);
if (new)
- new->vb.state = STATE_DONE;
+ new->vb.state = VIDEOBUF_DONE;
spin_lock_irqsave(&btv->s_lock,flags);
old = btv->screen;
btv->screen = new;
@@ -1844,7 +1772,7 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
}
/* alloc risc memory */
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
redo_dma_risc = 1;
if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
goto fail;
@@ -1854,7 +1782,7 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
if (0 != (rc = bttv_buffer_risc(btv,buf)))
goto fail;
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -1893,7 +1821,7 @@ buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct bttv_fh *fh = q->priv_data;
struct bttv *btv = fh->btv;
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue,&btv->capture);
if (!btv->curr.frame_irq) {
btv->loop_irq |= 1;
@@ -1916,399 +1844,259 @@ static struct videobuf_queue_ops bttv_video_qops = {
.buf_release = buffer_release,
};
-static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
+static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
{
- switch (cmd) {
- case BTTV_VERSION:
- return BTTV_VERSION_CODE;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ unsigned int i;
+ int err;
- /* *** v4l1 *** ************************************************ */
- case VIDIOCGFREQ:
- {
- unsigned long *freq = arg;
- *freq = btv->freq;
- return 0;
- }
- case VIDIOCSFREQ:
- {
- struct v4l2_frequency freq;
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- memset(&freq, 0, sizeof(freq));
- freq.frequency = *(unsigned long *)arg;
- mutex_lock(&btv->lock);
- freq.type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- btv->freq = *(unsigned long *)arg;
- bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,&freq);
- if (btv->has_matchbox && btv->radio_user)
- tea5757_set_freq(btv,*(unsigned long *)arg);
- mutex_unlock(&btv->lock);
- return 0;
- }
+ for (i = 0; i < BTTV_TVNORMS; i++)
+ if (*id & bttv_tvnorms[i].v4l2_id)
+ break;
+ if (i == BTTV_TVNORMS)
+ return -EINVAL;
- case VIDIOCGTUNER:
- {
- struct video_tuner *v = arg;
+ mutex_lock(&btv->lock);
+ set_tvnorm(btv, i);
+ mutex_unlock(&btv->lock);
- if (UNSET == bttv_tvcards[btv->c.type].tuner)
- return -EINVAL;
- if (v->tuner) /* Only tuner 0 */
- return -EINVAL;
- strcpy(v->name, "Television");
- v->rangelow = 0;
- v->rangehigh = 0x7FFFFFFF;
- v->flags = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
- v->mode = btv->tvnorm;
- v->signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
- bttv_call_i2c_clients(btv,cmd,v);
- return 0;
- }
- case VIDIOCSTUNER:
- {
- struct video_tuner *v = arg;
+ return 0;
+}
- if (v->tuner) /* Only tuner 0 */
- return -EINVAL;
- if (v->mode >= BTTV_TVNORMS)
- return -EINVAL;
+static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- mutex_lock(&btv->lock);
- set_tvnorm(btv,v->mode);
- bttv_call_i2c_clients(btv,cmd,v);
- mutex_unlock(&btv->lock);
- return 0;
- }
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+ *id = V4L2_STD_625_50;
+ else
+ *id = V4L2_STD_525_60;
+ return 0;
+}
- case VIDIOCGCHAN:
- {
- struct video_channel *v = arg;
- unsigned int channel = v->channel;
+static int bttv_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ unsigned int n;
- if (channel >= bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
- v->tuners=0;
- v->flags = VIDEO_VC_AUDIO;
- v->type = VIDEO_TYPE_CAMERA;
- v->norm = btv->tvnorm;
- if (channel == bttv_tvcards[btv->c.type].tuner) {
- strcpy(v->name,"Television");
- v->flags|=VIDEO_VC_TUNER;
- v->type=VIDEO_TYPE_TV;
- v->tuners=1;
- } else if (channel == btv->svhs) {
- strcpy(v->name,"S-Video");
- } else {
- sprintf(v->name,"Composite%d",channel);
- }
- return 0;
- }
- case VIDIOCSCHAN:
- {
- struct video_channel *v = arg;
- unsigned int channel = v->channel;
+ n = i->index;
- if (channel >= bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
- if (v->norm >= BTTV_TVNORMS)
- return -EINVAL;
+ if (n >= bttv_tvcards[btv->c.type].video_inputs)
+ return -EINVAL;
- mutex_lock(&btv->lock);
- if (channel == btv->input &&
- v->norm == btv->tvnorm) {
- /* nothing to do */
- mutex_unlock(&btv->lock);
- return 0;
- }
+ memset(i, 0, sizeof(*i));
- set_input(btv, v->channel, v->norm);
- mutex_unlock(&btv->lock);
- return 0;
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->audioset = 1;
+
+ if (i->index == bttv_tvcards[btv->c.type].tuner) {
+ sprintf(i->name, "Television");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->tuner = 0;
+ } else if (i->index == btv->svhs) {
+ sprintf(i->name, "S-Video");
+ } else {
+ sprintf(i->name, "Composite%d", i->index);
}
- case VIDIOCGAUDIO:
- {
- struct video_audio *v = arg;
+ if (i->index == btv->input) {
+ __u32 dstatus = btread(BT848_DSTATUS);
+ if (0 == (dstatus & BT848_DSTATUS_PRES))
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if (0 == (dstatus & BT848_DSTATUS_HLOC))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ }
- memset(v,0,sizeof(*v));
- strcpy(v->name,"Television");
- v->flags |= VIDEO_AUDIO_MUTABLE;
- v->mode = VIDEO_SOUND_MONO;
+ for (n = 0; n < BTTV_TVNORMS; n++)
+ i->std |= bttv_tvnorms[n].v4l2_id;
- mutex_lock(&btv->lock);
- bttv_call_i2c_clients(btv,cmd,v);
+ return 0;
+}
- /* card specific hooks */
- if (btv->audio_hook)
- btv->audio_hook(btv,v,0);
+static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- mutex_unlock(&btv->lock);
- return 0;
- }
- case VIDIOCSAUDIO:
- {
- struct video_audio *v = arg;
- unsigned int audio = v->audio;
+ *i = btv->input;
+ return 0;
+}
- if (audio >= bttv_tvcards[btv->c.type].audio_inputs)
- return -EINVAL;
+static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- mutex_lock(&btv->lock);
- audio_mute(btv, (v->flags&VIDEO_AUDIO_MUTE) ? 1 : 0);
- bttv_call_i2c_clients(btv,cmd,v);
+ int err;
- /* card specific hooks */
- if (btv->audio_hook)
- btv->audio_hook(btv,v,1);
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- mutex_unlock(&btv->lock);
- return 0;
- }
+ if (i > bttv_tvcards[btv->c.type].video_inputs)
+ return -EINVAL;
- /* *** v4l2 *** ************************************************ */
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *e = arg;
- unsigned int index = e->index;
+ mutex_lock(&btv->lock);
+ set_input(btv, i, btv->tvnorm);
+ mutex_unlock(&btv->lock);
+ return 0;
+}
- if (index >= BTTV_TVNORMS)
- return -EINVAL;
- v4l2_video_std_construct(e, bttv_tvnorms[e->index].v4l2_id,
- bttv_tvnorms[e->index].name);
- e->index = index;
- return 0;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
- *id = bttv_tvnorms[btv->tvnorm].v4l2_id;
- return 0;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- unsigned int i;
+static int bttv_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
- for (i = 0; i < BTTV_TVNORMS; i++)
- if (*id & bttv_tvnorms[i].v4l2_id)
- break;
- if (i == BTTV_TVNORMS)
- return -EINVAL;
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- mutex_lock(&btv->lock);
- set_tvnorm(btv,i);
- i2c_vidiocschan(btv);
- mutex_unlock(&btv->lock);
- return 0;
- }
- case VIDIOC_QUERYSTD:
- {
- v4l2_std_id *id = arg;
+ if (UNSET == bttv_tvcards[btv->c.type].tuner)
+ return -EINVAL;
- if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
- *id = V4L2_STD_625_50;
- else
- *id = V4L2_STD_525_60;
- return 0;
- }
+ if (0 != t->index)
+ return -EINVAL;
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
- unsigned int n;
+ mutex_lock(&btv->lock);
+ bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t);
- n = i->index;
- if (n >= bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
- memset(i,0,sizeof(*i));
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- i->audioset = 1;
- if (i->index == bttv_tvcards[btv->c.type].tuner) {
- sprintf(i->name, "Television");
- i->type = V4L2_INPUT_TYPE_TUNER;
- i->tuner = 0;
- } else if (i->index == btv->svhs) {
- sprintf(i->name, "S-Video");
- } else {
- sprintf(i->name,"Composite%d",i->index);
- }
- if (i->index == btv->input) {
- __u32 dstatus = btread(BT848_DSTATUS);
- if (0 == (dstatus & BT848_DSTATUS_PRES))
- i->status |= V4L2_IN_ST_NO_SIGNAL;
- if (0 == (dstatus & BT848_DSTATUS_HLOC))
- i->status |= V4L2_IN_ST_NO_H_LOCK;
- }
- for (n = 0; n < BTTV_TVNORMS; n++)
- i->std |= bttv_tvnorms[n].v4l2_id;
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = btv->input;
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- unsigned int *i = arg;
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 1);
- if (*i > bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
- mutex_lock(&btv->lock);
- set_input(btv, *i, btv->tvnorm);
- mutex_unlock(&btv->lock);
- return 0;
- }
+ mutex_unlock(&btv->lock);
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *t = arg;
+ return 0;
+}
- if (UNSET == bttv_tvcards[btv->c.type].tuner)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
- mutex_lock(&btv->lock);
- memset(t,0,sizeof(*t));
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
- strcpy(t->name, "Television");
- t->capability = V4L2_TUNER_CAP_NORM;
- t->type = V4L2_TUNER_ANALOG_TV;
- if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
- t->signal = 0xffff;
-
- if (btv->audio_hook) {
- /* Hmmm ... */
- struct video_audio va;
- memset(&va, 0, sizeof(struct video_audio));
- btv->audio_hook(btv,&va,0);
- t->audmode = V4L2_TUNER_MODE_MONO;
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- if(va.mode & VIDEO_SOUND_STEREO) {
- t->audmode = V4L2_TUNER_MODE_STEREO;
- t->rxsubchans = V4L2_TUNER_SUB_STEREO;
- }
- if(va.mode & VIDEO_SOUND_LANG2) {
- t->audmode = V4L2_TUNER_MODE_LANG1;
- t->rxsubchans = V4L2_TUNER_SUB_LANG1
- | V4L2_TUNER_SUB_LANG2;
- }
- }
- /* FIXME: fill capability+audmode */
- mutex_unlock(&btv->lock);
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *t = arg;
+static int bttv_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
- if (UNSET == bttv_tvcards[btv->c.type].tuner)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
- mutex_lock(&btv->lock);
- bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t);
- if (btv->audio_hook) {
- struct video_audio va;
- memset(&va, 0, sizeof(struct video_audio));
- if (t->audmode == V4L2_TUNER_MODE_MONO)
- va.mode = VIDEO_SOUND_MONO;
- else if (t->audmode == V4L2_TUNER_MODE_STEREO ||
- t->audmode == V4L2_TUNER_MODE_LANG1_LANG2)
- va.mode = VIDEO_SOUND_STEREO;
- else if (t->audmode == V4L2_TUNER_MODE_LANG1)
- va.mode = VIDEO_SOUND_LANG1;
- else if (t->audmode == V4L2_TUNER_MODE_LANG2)
- va.mode = VIDEO_SOUND_LANG2;
- btv->audio_hook(btv,&va,1);
- }
- mutex_unlock(&btv->lock);
- return 0;
- }
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = btv->freq;
- memset(f,0,sizeof(*f));
- f->type = V4L2_TUNER_ANALOG_TV;
- f->frequency = btv->freq;
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ return 0;
+}
- if (unlikely(f->tuner != 0))
- return -EINVAL;
- if (unlikely (f->type != V4L2_TUNER_ANALOG_TV))
- return -EINVAL;
- mutex_lock(&btv->lock);
- btv->freq = f->frequency;
- bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,f);
- if (btv->has_matchbox && btv->radio_user)
- tea5757_set_freq(btv,btv->freq);
- mutex_unlock(&btv->lock);
- return 0;
- }
- case VIDIOC_LOG_STATUS:
- {
- printk(KERN_INFO "bttv%d: ================= START STATUS CARD #%d =================\n", btv->c.nr, btv->c.nr);
- bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL);
- printk(KERN_INFO "bttv%d: ================== END STATUS CARD #%d ==================\n", btv->c.nr, btv->c.nr);
- return 0;
- }
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
- return -EINVAL;
- /* bt848 has a 12-bit register space */
- reg->reg &= 0xfff;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = btread(reg->reg);
- else
- btwrite(reg->val, reg->reg);
- return 0;
- }
-#endif
+static int bttv_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
- default:
- return -ENOIOCTLCMD;
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- }
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+ if (unlikely(f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ mutex_lock(&btv->lock);
+ btv->freq = f->frequency;
+ bttv_call_i2c_clients(btv, VIDIOC_S_FREQUENCY, f);
+ if (btv->has_matchbox && btv->radio_user)
+ tea5757_set_freq(btv, btv->freq);
+ mutex_unlock(&btv->lock);
return 0;
}
-/* Given cropping boundaries b and the scaled width and height of a
- single field or frame, which must not exceed hardware limits, this
- function adjusts the cropping parameters c. */
-static void
-bttv_crop_adjust (struct bttv_crop * c,
- const struct v4l2_rect * b,
- __s32 width,
- __s32 height,
- enum v4l2_field field)
+static int bttv_log_status(struct file *file, void *f)
{
- __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
- __s32 max_left;
- __s32 max_top;
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- if (width < c->min_scaled_width) {
- /* Max. hor. scale factor 16:1. */
- c->rect.width = width * 16;
- } else if (width > c->max_scaled_width) {
- /* Min. hor. scale factor 1:1. */
- c->rect.width = width;
+ printk(KERN_INFO "bttv%d: ======== START STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
+ bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL);
+ printk(KERN_INFO "bttv%d: ======== END STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
+ return 0;
+}
- max_left = b->left + b->width - width;
- max_left = min(max_left, (__s32) MAX_HDELAY);
- if (c->rect.left > max_left)
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bttv_g_register(struct file *file, void *f,
+ struct v4l2_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ reg->val = btread(reg->reg);
+
+ return 0;
+}
+
+static int bttv_s_register(struct file *file, void *f,
+ struct v4l2_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ btwrite(reg->val, reg->reg);
+
+ return 0;
+}
+#endif
+
+/* Given cropping boundaries b and the scaled width and height of a
+ single field or frame, which must not exceed hardware limits, this
+ function adjusts the cropping parameters c. */
+static void
+bttv_crop_adjust (struct bttv_crop * c,
+ const struct v4l2_rect * b,
+ __s32 width,
+ __s32 height,
+ enum v4l2_field field)
+{
+ __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
+ __s32 max_left;
+ __s32 max_top;
+
+ if (width < c->min_scaled_width) {
+ /* Max. hor. scale factor 16:1. */
+ c->rect.width = width * 16;
+ } else if (width > c->max_scaled_width) {
+ /* Min. hor. scale factor 1:1. */
+ c->rect.width = width;
+
+ max_left = b->left + b->width - width;
+ max_left = min(max_left, (__s32) MAX_HDELAY);
+ if (c->rect.left > max_left)
c->rect.left = max_left;
}
@@ -2659,983 +2447,681 @@ pix_format_set_size (struct v4l2_pix_format * f,
}
}
-static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f)
+static int bttv_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format));
- pix_format_set_size (&f->fmt.pix, fh->fmt,
- fh->width, fh->height);
- f->fmt.pix.field = fh->cap.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
- return 0;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- memset(&f->fmt.win,0,sizeof(struct v4l2_window));
- f->fmt.win.w = fh->ov.w;
- f->fmt.win.field = fh->ov.field;
- return 0;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- bttv_vbi_get_fmt(fh, &f->fmt.vbi);
- return 0;
- default:
- return -EINVAL;
- }
+ struct bttv_fh *fh = priv;
+
+ pix_format_set_size(&f->fmt.pix, fh->fmt,
+ fh->width, fh->height);
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+
+ return 0;
}
-static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv,
- struct v4l2_format *f, int adjust_crop)
+static int bttv_g_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- {
- const struct bttv_format *fmt;
- enum v4l2_field field;
- __s32 width, height;
- int rc;
+ struct bttv_fh *fh = priv;
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
+ f->fmt.win.w = fh->ov.w;
+ f->fmt.win.field = fh->ov.field;
- field = f->fmt.pix.field;
- if (V4L2_FIELD_ANY == field) {
- __s32 height2;
+ return 0;
+}
- height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
- field = (f->fmt.pix.height > height2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
- if (V4L2_FIELD_SEQ_BT == field)
- field = V4L2_FIELD_SEQ_TB;
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- case V4L2_FIELD_INTERLACED:
- break;
- case V4L2_FIELD_SEQ_TB:
- if (fmt->flags & FORMAT_FLAGS_PLANAR)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
+static int bttv_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ enum v4l2_field field;
+ __s32 width, height;
+ int rc;
- width = f->fmt.pix.width;
- height = f->fmt.pix.height;
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
- rc = limit_scaled_size(fh, &width, &height, field,
- /* width_mask: 4 pixels */ ~3,
- /* width_bias: nearest */ 2,
- /* adjust_size */ 1,
- adjust_crop);
- if (0 != rc)
- return rc;
+ field = f->fmt.pix.field;
- /* update data for the application */
- f->fmt.pix.field = field;
- pix_format_set_size(&f->fmt.pix, fmt, width, height);
+ if (V4L2_FIELD_ANY == field) {
+ __s32 height2;
- return 0;
+ height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+ field = (f->fmt.pix.height > height2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
}
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- return verify_window(fh, &f->fmt.win,
- /* adjust_size */ 1,
- /* adjust_crop */ 0);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- return bttv_vbi_try_fmt(fh, &f->fmt.vbi);
+
+ if (V4L2_FIELD_SEQ_BT == field)
+ field = V4L2_FIELD_SEQ_TB;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ if (fmt->flags & FORMAT_FLAGS_PLANAR)
+ return -EINVAL;
+ break;
default:
return -EINVAL;
}
+
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+
+ rc = limit_scaled_size(fh, &width, &height, field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+ if (0 != rc)
+ return rc;
+
+ /* update data for the application */
+ f->fmt.pix.field = field;
+ pix_format_set_size(&f->fmt.pix, fmt, width, height);
+
+ return 0;
+}
+
+static int bttv_try_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ return verify_window(fh, &f->fmt.win,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
}
-static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv,
- struct v4l2_format *f)
+static int bttv_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
int retval;
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ __s32 width, height;
+ enum v4l2_field field;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- {
- const struct bttv_format *fmt;
+ retval = bttv_switch_type(fh, f->type);
+ if (0 != retval)
+ return retval;
- retval = bttv_switch_type(fh,f->type);
- if (0 != retval)
- return retval;
- retval = bttv_try_fmt(fh,btv,f, /* adjust_crop */ 1);
- if (0 != retval)
- return retval;
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ retval = bttv_try_fmt_cap(file, priv, f);
+ if (0 != retval)
+ return retval;
- /* update our state informations */
- mutex_lock(&fh->cap.lock);
- fh->fmt = fmt;
- fh->cap.field = f->fmt.pix.field;
- fh->cap.last = V4L2_FIELD_NONE;
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- btv->init.fmt = fmt;
- btv->init.width = f->fmt.pix.width;
- btv->init.height = f->fmt.pix.height;
- mutex_unlock(&fh->cap.lock);
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+ field = f->fmt.pix.field;
- return 0;
- }
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- return setup_window(fh, btv, &f->fmt.win, 1);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- retval = bttv_switch_type(fh,f->type);
- if (0 != retval)
- return retval;
- return bttv_vbi_set_fmt(fh, &f->fmt.vbi);
- default:
+ retval = limit_scaled_size(fh, &width, &height, f->fmt.pix.field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 1);
+ if (0 != retval)
+ return retval;
+
+ f->fmt.pix.field = field;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+ /* update our state informations */
+ mutex_lock(&fh->cap.lock);
+ fh->fmt = fmt;
+ fh->cap.field = f->fmt.pix.field;
+ fh->cap.last = V4L2_FIELD_NONE;
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ btv->init.fmt = fmt;
+ btv->init.width = f->fmt.pix.width;
+ btv->init.height = f->fmt.pix.height;
+ mutex_unlock(&fh->cap.lock);
+
+ return 0;
+}
+
+static int bttv_s_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
+
+ return setup_window(fh, btv, &f->fmt.win, 1);
}
-static int bttv_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
{
- struct bttv_fh *fh = file->private_data;
- struct bttv *btv = fh->btv;
- unsigned long flags;
- int retval = 0;
+ int retval;
+ unsigned int i;
+ struct bttv_fh *fh = priv;
- if (bttv_debug > 1)
- v4l_print_ioctl(btv->c.name, cmd);
-
- if (btv->errors)
- bttv_reinit_bt848(btv);
-
- switch (cmd) {
- case VIDIOCSFREQ:
- case VIDIOCSTUNER:
- case VIDIOCSCHAN:
- case VIDIOC_S_CTRL:
- case VIDIOC_S_STD:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_TUNER:
- case VIDIOC_S_FREQUENCY:
- retval = v4l2_prio_check(&btv->prio,&fh->prio);
- if (0 != retval)
- return retval;
- };
+ mutex_lock(&fh->cap.lock);
+ retval = videobuf_mmap_setup(&fh->cap, gbuffers, gbufsize,
+ V4L2_MEMORY_MMAP);
+ if (retval < 0) {
+ mutex_unlock(&fh->cap.lock);
+ return retval;
+ }
- switch (cmd) {
+ gbuffers = retval;
+ memset(mbuf, 0, sizeof(*mbuf));
+ mbuf->frames = gbuffers;
+ mbuf->size = gbuffers * gbufsize;
- /* *** v4l1 *** ************************************************ */
- case VIDIOCGCAP:
- {
- struct video_capability *cap = arg;
+ for (i = 0; i < gbuffers; i++)
+ mbuf->offsets[i] = i * gbufsize;
- memset(cap,0,sizeof(*cap));
- strcpy(cap->name,btv->video_dev->name);
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
- /* vbi */
- cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT;
- } else {
- /* others */
- cap->type = VID_TYPE_CAPTURE|
- VID_TYPE_TUNER|
- VID_TYPE_CLIPPING|
- VID_TYPE_SCALES;
- if (no_overlay <= 0)
- cap->type |= VID_TYPE_OVERLAY;
-
- cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth;
- cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
- cap->minwidth = 48;
- cap->minheight = 32;
- }
- cap->channels = bttv_tvcards[btv->c.type].video_inputs;
- cap->audios = bttv_tvcards[btv->c.type].audio_inputs;
- return 0;
- }
+ mutex_unlock(&fh->cap.lock);
+ return 0;
+}
+#endif
- case VIDIOCGPICT:
- {
- struct video_picture *pic = arg;
-
- memset(pic,0,sizeof(*pic));
- pic->brightness = btv->bright;
- pic->contrast = btv->contrast;
- pic->hue = btv->hue;
- pic->colour = btv->saturation;
- if (fh->fmt) {
- pic->depth = fh->fmt->depth;
- pic->palette = fh->fmt->palette;
- }
- return 0;
- }
- case VIDIOCSPICT:
- {
- struct video_picture *pic = arg;
- const struct bttv_format *fmt;
+static int bttv_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- fmt = format_by_palette(pic->palette);
- if (NULL == fmt)
- return -EINVAL;
- mutex_lock(&fh->cap.lock);
- if (fmt->flags & FORMAT_FLAGS_RAW) {
- /* VIDIOCMCAPTURE uses gbufsize, not RAW_BPL *
- RAW_LINES * 2. F1 is stored at offset 0, F2
- at buffer size / 2. */
- fh->width = RAW_BPL;
- fh->height = gbufsize / RAW_BPL;
- btv->init.width = RAW_BPL;
- btv->init.height = gbufsize / RAW_BPL;
- }
- fh->ovfmt = fmt;
- fh->fmt = fmt;
- btv->init.ovfmt = fmt;
- btv->init.fmt = fmt;
- if (bigendian) {
- /* dirty hack time: swap bytes for overlay if the
- display adaptor is big endian (insmod option) */
- if (fmt->palette == VIDEO_PALETTE_RGB555 ||
- fmt->palette == VIDEO_PALETTE_RGB565 ||
- fmt->palette == VIDEO_PALETTE_RGB32) {
- fh->ovfmt = fmt+1;
- }
- }
- bt848_bright(btv,pic->brightness);
- bt848_contrast(btv,pic->contrast);
- bt848_hue(btv,pic->hue);
- bt848_sat(btv,pic->colour);
- mutex_unlock(&fh->cap.lock);
- return 0;
- }
+ if (0 == v4l2)
+ return -EINVAL;
- case VIDIOCGWIN:
- {
- struct video_window *win = arg;
+ strlcpy(cap->driver, "bttv", sizeof(cap->driver));
+ strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "PCI:%s", pci_name(btv->c.pci));
+ cap->version = BTTV_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if (no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ if (bttv_tvcards[btv->c.type].tuner != UNSET &&
+ bttv_tvcards[btv->c.type].tuner != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
- memset(win,0,sizeof(*win));
- win->x = fh->ov.w.left;
- win->y = fh->ov.w.top;
- win->width = fh->ov.w.width;
- win->height = fh->ov.w.height;
- return 0;
- }
- case VIDIOCSWIN:
- {
- struct video_window *win = arg;
- struct v4l2_window w2;
+static int bttv_enum_fmt_vbi(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (0 != f->index)
+ return -EINVAL;
- if (no_overlay > 0) {
- printk ("VIDIOCSWIN: no_overlay\n");
- return -EINVAL;
- }
+ f->pixelformat = V4L2_PIX_FMT_GREY;
+ strcpy(f->description, "vbi data");
- w2.field = V4L2_FIELD_ANY;
- w2.w.left = win->x;
- w2.w.top = win->y;
- w2.w.width = win->width;
- w2.w.height = win->height;
- w2.clipcount = win->clipcount;
- w2.clips = (struct v4l2_clip __user *)win->clips;
- retval = setup_window(fh, btv, &w2, 0);
- if (0 == retval) {
- /* on v4l1 this ioctl affects the read() size too */
- fh->width = fh->ov.w.width;
- fh->height = fh->ov.w.height;
- btv->init.width = fh->ov.w.width;
- btv->init.height = fh->ov.w.height;
- }
- return retval;
- }
+ return 0;
+}
- case VIDIOCGFBUF:
- {
- struct video_buffer *fbuf = arg;
-
- fbuf->base = btv->fbuf.base;
- fbuf->width = btv->fbuf.fmt.width;
- fbuf->height = btv->fbuf.fmt.height;
- fbuf->bytesperline = btv->fbuf.fmt.bytesperline;
- if (fh->ovfmt)
- fbuf->depth = fh->ovfmt->depth;
- else {
- if (fbuf->width)
- fbuf->depth = ((fbuf->bytesperline<<3)
- + (fbuf->width-1) )
- /fbuf->width;
- else
- fbuf->depth = 0;
- }
- return 0;
- }
- case VIDIOCSFBUF:
- {
- struct video_buffer *fbuf = arg;
- const struct bttv_format *fmt;
- unsigned long end;
-
- if(!capable(CAP_SYS_ADMIN) &&
- !capable(CAP_SYS_RAWIO))
- return -EPERM;
- end = (unsigned long)fbuf->base +
- fbuf->height * fbuf->bytesperline;
- mutex_lock(&fh->cap.lock);
- retval = -EINVAL;
+static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+{
+ int index = -1, i;
- switch (fbuf->depth) {
- case 8:
- fmt = format_by_palette(VIDEO_PALETTE_HI240);
- break;
- case 16:
- fmt = format_by_palette(VIDEO_PALETTE_RGB565);
- break;
- case 24:
- fmt = format_by_palette(VIDEO_PALETTE_RGB24);
- break;
- case 32:
- fmt = format_by_palette(VIDEO_PALETTE_RGB32);
- break;
- case 15:
- fbuf->depth = 16;
- fmt = format_by_palette(VIDEO_PALETTE_RGB555);
- break;
- default:
- fmt = NULL;
+ for (i = 0; i < FORMATS; i++) {
+ if (formats[i].fourcc != -1)
+ index++;
+ if ((unsigned int)index == f->index)
break;
- }
- if (NULL == fmt)
- goto fh_unlock_and_return;
-
- fh->ovfmt = fmt;
- fh->fmt = fmt;
- btv->init.ovfmt = fmt;
- btv->init.fmt = fmt;
- btv->fbuf.base = fbuf->base;
- btv->fbuf.fmt.width = fbuf->width;
- btv->fbuf.fmt.height = fbuf->height;
- if (fbuf->bytesperline)
- btv->fbuf.fmt.bytesperline = fbuf->bytesperline;
- else
- btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fbuf->depth/8;
- mutex_unlock(&fh->cap.lock);
- return 0;
}
+ if (FORMATS == i)
+ return -EINVAL;
- case VIDIOCCAPTURE:
- case VIDIOC_OVERLAY:
- {
- struct bttv_buffer *new;
- int *on = arg;
+ f->pixelformat = formats[i].fourcc;
+ strlcpy(f->description, formats[i].name, sizeof(f->description));
- if (*on) {
- /* verify args */
- if (NULL == btv->fbuf.base)
- return -EINVAL;
- if (!fh->ov.setup_ok) {
- dprintk("bttv%d: overlay: !setup_ok\n",btv->c.nr);
- return -EINVAL;
- }
- }
+ return i;
+}
- if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
- return -EBUSY;
+static int bttv_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc = bttv_enum_fmt_cap_ovr(f);
- mutex_lock(&fh->cap.lock);
- if (*on) {
- fh->ov.tvnorm = btv->tvnorm;
- new = videobuf_pci_alloc(sizeof(*new));
- new->crop = btv->crop[!!fh->do_crop].rect;
- bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
- } else {
- new = NULL;
- }
+ if (rc < 0)
+ return rc;
- /* switch over */
- retval = bttv_switch_overlay(btv,fh,new);
- mutex_unlock(&fh->cap.lock);
- return retval;
+ return 0;
+}
+
+static int bttv_enum_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc;
+
+ if (no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
}
- case VIDIOCGMBUF:
- {
- struct video_mbuf *mbuf = arg;
- unsigned int i;
+ rc = bttv_enum_fmt_cap_ovr(f);
- retval = videobuf_mmap_setup(&fh->cap,gbuffers,gbufsize,
- V4L2_MEMORY_MMAP);
- if (retval < 0)
- return retval;
+ if (rc < 0)
+ return rc;
- gbuffers = retval;
- memset(mbuf,0,sizeof(*mbuf));
- mbuf->frames = gbuffers;
- mbuf->size = gbuffers * gbufsize;
- for (i = 0; i < gbuffers; i++)
- mbuf->offsets[i] = i * gbufsize;
- return 0;
- }
- case VIDIOCMCAPTURE:
- {
- struct video_mmap *vm = arg;
- struct bttv_buffer *buf;
- enum v4l2_field field;
- __s32 height2;
- int res;
+ if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
- if (vm->frame >= VIDEO_MAX_FRAME)
- return -EINVAL;
+ return 0;
+}
- res = bttv_resource(fh);
- if (!check_alloc_btres(btv, fh, res))
- return -EBUSY;
+static int bttv_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- mutex_lock(&fh->cap.lock);
- retval = -EINVAL;
- buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame];
- if (NULL == buf)
- goto fh_unlock_and_return;
- if (0 == buf->vb.baddr)
- goto fh_unlock_and_return;
- if (buf->vb.state == STATE_QUEUED ||
- buf->vb.state == STATE_ACTIVE)
- goto fh_unlock_and_return;
+ *fb = btv->fbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ if (fh->ovfmt)
+ fb->fmt.pixelformat = fh->ovfmt->fourcc;
+ return 0;
+}
- height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
- field = (vm->height > height2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- retval = bttv_prepare_buffer(&fh->cap,btv,buf,
- format_by_palette(vm->format),
- vm->width,vm->height,field);
- if (0 != retval)
- goto fh_unlock_and_return;
- btv->init.width = vm->width;
- btv->init.height = vm->height;
- spin_lock_irqsave(&btv->s_lock,flags);
- buffer_queue(&fh->cap,&buf->vb);
- spin_unlock_irqrestore(&btv->s_lock,flags);
- mutex_unlock(&fh->cap.lock);
- return 0;
- }
- case VIDIOCSYNC:
- {
- int *frame = arg;
- struct bttv_buffer *buf;
+static int bttv_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *new;
+ int retval;
- if (*frame >= VIDEO_MAX_FRAME)
+ if (on) {
+ /* verify args */
+ if (NULL == btv->fbuf.base)
return -EINVAL;
+ if (!fh->ov.setup_ok) {
+ dprintk("bttv%d: overlay: !setup_ok\n", btv->c.nr);
+ return -EINVAL;
+ }
+ }
- mutex_lock(&fh->cap.lock);
- retval = -EINVAL;
- buf = (struct bttv_buffer *)fh->cap.bufs[*frame];
- if (NULL == buf)
- goto fh_unlock_and_return;
- retval = videobuf_waiton(&buf->vb,0,1);
+ if (!check_alloc_btres(btv, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+
+ mutex_lock(&fh->cap.lock);
+ if (on) {
+ fh->ov.tvnorm = btv->tvnorm;
+ new = videobuf_pci_alloc(sizeof(*new));
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ } else {
+ new = NULL;
+ }
+
+ /* switch over */
+ retval = bttv_switch_overlay(btv, fh, new);
+ mutex_unlock(&fh->cap.lock);
+ return retval;
+}
+
+static int bttv_s_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_format *fmt;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+ if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+
+ retval = -EINVAL;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ __s32 width = fb->fmt.width;
+ __s32 height = fb->fmt.height;
+
+ retval = limit_scaled_size(fh, &width, &height,
+ V4L2_FIELD_INTERLACED,
+ /* width_mask */ ~3,
+ /* width_bias */ 2,
+ /* adjust_size */ 0,
+ /* adjust_crop */ 0);
if (0 != retval)
- goto fh_unlock_and_return;
- switch (buf->vb.state) {
- case STATE_ERROR:
- retval = -EIO;
- /* fall through */
- case STATE_DONE:
- {
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- videobuf_dma_sync(&fh->cap,dma);
- bttv_dma_free(&fh->cap,btv,buf);
- break;
- }
- default:
- retval = -EINVAL;
- break;
- }
- mutex_unlock(&fh->cap.lock);
- return retval;
+ return retval;
}
- case VIDIOCGVBIFMT:
- if (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) {
- retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
- if (0 != retval)
- return retval;
- }
+ /* ok, accept it */
+ mutex_lock(&fh->cap.lock);
+ btv->fbuf.base = fb->base;
+ btv->fbuf.fmt.width = fb->fmt.width;
+ btv->fbuf.fmt.height = fb->fmt.height;
+ if (0 != fb->fmt.bytesperline)
+ btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+ else
+ btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
- /* fall through */
+ retval = 0;
+ fh->ovfmt = fmt;
+ btv->init.ovfmt = fmt;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ fh->ov.w.left = 0;
+ fh->ov.w.top = 0;
+ fh->ov.w.width = fb->fmt.width;
+ fh->ov.w.height = fb->fmt.height;
+ btv->init.ov.w.width = fb->fmt.width;
+ btv->init.ov.w.height = fb->fmt.height;
+ kfree(fh->ov.clips);
+ fh->ov.clips = NULL;
+ fh->ov.nclips = 0;
+
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_pci_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv, fh, new);
+ }
+ }
+ mutex_unlock(&fh->cap.lock);
+ return retval;
+}
- case VIDIOCSVBIFMT:
- return v4l_compat_translate_ioctl(inode, file, cmd,
- arg, bttv_do_ioctl);
-
- case BTTV_VERSION:
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- case VIDIOCGTUNER:
- case VIDIOCSTUNER:
- case VIDIOCGCHAN:
- case VIDIOCSCHAN:
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- return bttv_common_ioctls(btv,cmd,arg);
-
- /* *** v4l2 *** ************************************************ */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
+static int bttv_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_reqbufs(bttv_queue(fh), p);
+}
- if (0 == v4l2)
- return -EINVAL;
- memset(cap, 0, sizeof (*cap));
- strlcpy(cap->driver, "bttv", sizeof (cap->driver));
- strlcpy(cap->card, btv->video_dev->name, sizeof (cap->card));
- snprintf(cap->bus_info, sizeof (cap->bus_info),
- "PCI:%s", pci_name(btv->c.pci));
- cap->version = BTTV_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- if (no_overlay <= 0)
- cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
-
- if (bttv_tvcards[btv->c.type].tuner != UNSET &&
- bttv_tvcards[btv->c.type].tuner != TUNER_ABSENT)
- cap->capabilities |= V4L2_CAP_TUNER;
- return 0;
- }
+static int bttv_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_querybuf(bttv_queue(fh), b);
+}
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *f = arg;
- enum v4l2_buf_type type;
- unsigned int i;
- int index;
-
- type = f->type;
- if (V4L2_BUF_TYPE_VBI_CAPTURE == type) {
- /* vbi */
- index = f->index;
- if (0 != index)
- return -EINVAL;
- memset(f,0,sizeof(*f));
- f->index = index;
- f->type = type;
- f->pixelformat = V4L2_PIX_FMT_GREY;
- strcpy(f->description,"vbi data");
- return 0;
- }
+static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
- /* video capture + overlay */
- index = -1;
- for (i = 0; i < BTTV_FORMATS; i++) {
- if (bttv_formats[i].fourcc != -1)
- index++;
- if ((unsigned int)index == f->index)
- break;
- }
- if (BTTV_FORMATS == i)
- return -EINVAL;
+ if (!check_alloc_btres(btv, fh, res))
+ return -EBUSY;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (!(bttv_formats[i].flags & FORMAT_FLAGS_PACKED))
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- memset(f,0,sizeof(*f));
- f->index = index;
- f->type = type;
- f->pixelformat = bttv_formats[i].fourcc;
- strlcpy(f->description,bttv_formats[i].name,sizeof(f->description));
- return 0;
- }
+ return videobuf_qbuf(bttv_queue(fh), b);
+}
- case VIDIOC_TRY_FMT:
- {
- struct v4l2_format *f = arg;
- return bttv_try_fmt(fh,btv,f, /* adjust_crop */ 0);
- }
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *f = arg;
- return bttv_g_fmt(fh,f);
- }
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *f = arg;
- return bttv_s_fmt(fh,btv,f);
- }
+static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_dqbuf(bttv_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
- case VIDIOC_G_FBUF:
- {
- struct v4l2_framebuffer *fb = arg;
+static int bttv_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
- *fb = btv->fbuf;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- if (fh->ovfmt)
- fb->fmt.pixelformat = fh->ovfmt->fourcc;
- return 0;
- }
- case VIDIOC_S_FBUF:
- {
- struct v4l2_framebuffer *fb = arg;
- const struct bttv_format *fmt;
+ if (!check_alloc_btres(btv, fh, res))
+ return -EBUSY;
+ return videobuf_streamon(bttv_queue(fh));
+}
- if(!capable(CAP_SYS_ADMIN) &&
- !capable(CAP_SYS_RAWIO))
- return -EPERM;
- /* check args */
- fmt = format_by_fourcc(fb->fmt.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
- if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
- return -EINVAL;
+static int bttv_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int retval;
+ int res = bttv_resource(fh);
- retval = -EINVAL;
- if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
- __s32 width = fb->fmt.width;
- __s32 height = fb->fmt.height;
-
- retval = limit_scaled_size(fh, &width, &height,
- V4L2_FIELD_INTERLACED,
- /* width_mask */ ~3,
- /* width_bias */ 2,
- /* adjust_size */ 0,
- /* adjust_crop */ 0);
- if (0 != retval)
- return retval;
- }
- /* ok, accept it */
- mutex_lock(&fh->cap.lock);
- btv->fbuf.base = fb->base;
- btv->fbuf.fmt.width = fb->fmt.width;
- btv->fbuf.fmt.height = fb->fmt.height;
- if (0 != fb->fmt.bytesperline)
- btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
- else
- btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
-
- retval = 0;
- fh->ovfmt = fmt;
- btv->init.ovfmt = fmt;
- if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
- fh->ov.w.left = 0;
- fh->ov.w.top = 0;
- fh->ov.w.width = fb->fmt.width;
- fh->ov.w.height = fb->fmt.height;
- btv->init.ov.w.width = fb->fmt.width;
- btv->init.ov.w.height = fb->fmt.height;
- kfree(fh->ov.clips);
- fh->ov.clips = NULL;
- fh->ov.nclips = 0;
-
- if (check_btres(fh, RESOURCE_OVERLAY)) {
- struct bttv_buffer *new;
-
- new = videobuf_pci_alloc(sizeof(*new));
- new->crop = btv->crop[!!fh->do_crop].rect;
- bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);
- retval = bttv_switch_overlay(btv,fh,new);
- }
- }
- mutex_unlock(&fh->cap.lock);
+ retval = videobuf_streamoff(bttv_queue(fh));
+ if (retval < 0)
return retval;
- }
+ free_btres(btv, fh, res);
+ return 0;
+}
- case VIDIOC_REQBUFS:
- return videobuf_reqbufs(bttv_queue(fh),arg);
+static int bttv_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_queryctrl *ctrl;
- case VIDIOC_QUERYBUF:
- return videobuf_querybuf(bttv_queue(fh),arg);
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
- case VIDIOC_QBUF:
- {
- int res = bttv_resource(fh);
+ if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+ *c = no_ctl;
+ else {
+ ctrl = ctrl_by_id(c->id);
- if (!check_alloc_btres(btv, fh, res))
- return -EBUSY;
- return videobuf_qbuf(bttv_queue(fh),arg);
+ *c = (NULL != ctrl) ? *ctrl : no_ctl;
}
- case VIDIOC_DQBUF:
- return videobuf_dqbuf(bttv_queue(fh),arg,
- file->f_flags & O_NONBLOCK);
+ return 0;
+}
- case VIDIOC_STREAMON:
- {
- int res = bttv_resource(fh);
+static int bttv_g_parm(struct file *file, void *f,
+ struct v4l2_streamparm *parm)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ struct v4l2_standard s;
- if (!check_alloc_btres(btv,fh,res))
- return -EBUSY;
- return videobuf_streamon(bttv_queue(fh));
- }
- case VIDIOC_STREAMOFF:
- {
- int res = bttv_resource(fh);
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id,
+ bttv_tvnorms[btv->tvnorm].name);
+ parm->parm.capture.timeperframe = s.frameperiod;
+ return 0;
+}
- retval = videobuf_streamoff(bttv_queue(fh));
- if (retval < 0)
- return retval;
- free_btres(btv,fh,res);
- return 0;
- }
+static int bttv_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *c = arg;
- int i;
+ if (UNSET == bttv_tvcards[btv->c.type].tuner)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
- if ((c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1) &&
- (c->id < V4L2_CID_PRIVATE_BASE ||
- c->id >= V4L2_CID_PRIVATE_LASTP1))
- return -EINVAL;
- for (i = 0; i < BTTV_CTLS; i++)
- if (bttv_ctls[i].id == c->id)
- break;
- if (i == BTTV_CTLS) {
- *c = no_ctl;
- return 0;
- }
- *c = bttv_ctls[i];
- if (btv->audio_hook && i >= 4 && i <= 8) {
- struct video_audio va;
- memset(&va,0,sizeof(va));
- btv->audio_hook(btv,&va,0);
- switch (bttv_ctls[i].id) {
- case V4L2_CID_AUDIO_VOLUME:
- if (!(va.flags & VIDEO_AUDIO_VOLUME))
- *c = no_ctl;
- break;
- case V4L2_CID_AUDIO_BALANCE:
- if (!(va.flags & VIDEO_AUDIO_BALANCE))
- *c = no_ctl;
- break;
- case V4L2_CID_AUDIO_BASS:
- if (!(va.flags & VIDEO_AUDIO_BASS))
- *c = no_ctl;
- break;
- case V4L2_CID_AUDIO_TREBLE:
- if (!(va.flags & VIDEO_AUDIO_TREBLE))
- *c = no_ctl;
- break;
- }
- }
- return 0;
- }
- case VIDIOC_G_CTRL:
- return get_control(btv,arg);
- case VIDIOC_S_CTRL:
- return set_control(btv,arg);
- case VIDIOC_G_PARM:
- {
- struct v4l2_streamparm *parm = arg;
- struct v4l2_standard s;
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- memset(parm,0,sizeof(*parm));
- v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id,
- bttv_tvnorms[btv->tvnorm].name);
- parm->parm.capture.timeperframe = s.frameperiod;
- return 0;
- }
+ mutex_lock(&btv->lock);
+ memset(t, 0, sizeof(*t));
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->type = V4L2_TUNER_ANALOG_TV;
+ if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+ t->signal = 0xffff;
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
- case VIDIOC_G_PRIORITY:
- {
- enum v4l2_priority *p = arg;
+ mutex_unlock(&btv->lock);
+ return 0;
+}
- *p = v4l2_prio_max(&btv->prio);
- return 0;
- }
- case VIDIOC_S_PRIORITY:
- {
- enum v4l2_priority *prio = arg;
+static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- return v4l2_prio_change(&btv->prio, &fh->prio, *prio);
- }
+ *p = v4l2_prio_max(&btv->prio);
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *cap = arg;
- enum v4l2_buf_type type;
+ return 0;
+}
- type = cap->type;
+static int bttv_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
+ return v4l2_prio_change(&btv->prio, &fh->prio, prio);
+}
- *cap = bttv_tvnorms[btv->tvnorm].cropcap;
- cap->type = type;
+static int bttv_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- return 0;
- }
- case VIDIOC_G_CROP:
- {
- struct v4l2_crop * crop = arg;
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
+ *cap = bttv_tvnorms[btv->tvnorm].cropcap;
- /* No fh->do_crop = 1; because btv->crop[1] may be
- inconsistent with fh->width or fh->height and apps
- do not expect a change here. */
+ return 0;
+}
- crop->c = btv->crop[!!fh->do_crop].rect;
+static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- return 0;
- }
- case VIDIOC_S_CROP:
- {
- struct v4l2_crop *crop = arg;
- const struct v4l2_rect *b;
- struct bttv_crop c;
- __s32 b_left;
- __s32 b_top;
- __s32 b_right;
- __s32 b_bottom;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
- retval = v4l2_prio_check(&btv->prio,&fh->prio);
- if (0 != retval)
- return retval;
+ /* No fh->do_crop = 1; because btv->crop[1] may be
+ inconsistent with fh->width or fh->height and apps
+ do not expect a change here. */
- /* Make sure tvnorm, vbi_end and the current cropping
- parameters remain consistent until we're done. Note
- read() may change vbi_end in check_alloc_btres(). */
- mutex_lock(&btv->lock);
+ crop->c = btv->crop[!!fh->do_crop].rect;
- retval = -EBUSY;
+ return 0;
+}
- if (locked_btres(fh->btv, VIDEO_RESOURCES))
- goto btv_unlock_and_return;
+static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_rect *b;
+ int retval;
+ struct bttv_crop c;
+ __s32 b_left;
+ __s32 b_top;
+ __s32 b_right;
+ __s32 b_bottom;
- b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
- b_left = b->left;
- b_right = b_left + b->width;
- b_bottom = b->top + b->height;
+ retval = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != retval)
+ return retval;
- b_top = max(b->top, btv->vbi_end);
- if (b_top + 32 >= b_bottom)
- goto btv_unlock_and_return;
+ /* Make sure tvnorm, vbi_end and the current cropping
+ parameters remain consistent until we're done. Note
+ read() may change vbi_end in check_alloc_btres(). */
+ mutex_lock(&btv->lock);
- /* Min. scaled size 48 x 32. */
- c.rect.left = clamp(crop->c.left, b_left, b_right - 48);
- c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
+ retval = -EBUSY;
- c.rect.width = clamp(crop->c.width,
- 48, b_right - c.rect.left);
+ if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+ mutex_unlock(&btv->lock);
+ return retval;
+ }
- c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32);
- /* Top and height must be a multiple of two. */
- c.rect.top = (c.rect.top + 1) & ~1;
+ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
- c.rect.height = clamp(crop->c.height,
- 32, b_bottom - c.rect.top);
- c.rect.height = (c.rect.height + 1) & ~1;
+ b_left = b->left;
+ b_right = b_left + b->width;
+ b_bottom = b->top + b->height;
- bttv_crop_calc_limits(&c);
+ b_top = max(b->top, btv->vbi_end);
+ if (b_top + 32 >= b_bottom) {
+ mutex_unlock(&btv->lock);
+ return retval;
+ }
- btv->crop[1] = c;
+ /* Min. scaled size 48 x 32. */
+ c.rect.left = clamp(crop->c.left, b_left, b_right - 48);
+ c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
- mutex_unlock(&btv->lock);
+ c.rect.width = clamp(crop->c.width,
+ 48, b_right - c.rect.left);
- fh->do_crop = 1;
+ c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32);
+ /* Top and height must be a multiple of two. */
+ c.rect.top = (c.rect.top + 1) & ~1;
- mutex_lock(&fh->cap.lock);
+ c.rect.height = clamp(crop->c.height,
+ 32, b_bottom - c.rect.top);
+ c.rect.height = (c.rect.height + 1) & ~1;
- if (fh->width < c.min_scaled_width) {
- fh->width = c.min_scaled_width;
- btv->init.width = c.min_scaled_width;
- } else if (fh->width > c.max_scaled_width) {
- fh->width = c.max_scaled_width;
- btv->init.width = c.max_scaled_width;
- }
+ bttv_crop_calc_limits(&c);
- if (fh->height < c.min_scaled_height) {
- fh->height = c.min_scaled_height;
- btv->init.height = c.min_scaled_height;
- } else if (fh->height > c.max_scaled_height) {
- fh->height = c.max_scaled_height;
- btv->init.height = c.max_scaled_height;
- }
+ btv->crop[1] = c;
- mutex_unlock(&fh->cap.lock);
+ mutex_unlock(&btv->lock);
- return 0;
- }
+ fh->do_crop = 1;
- case VIDIOC_ENUMSTD:
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
- case VIDIOC_ENUMINPUT:
- case VIDIOC_G_INPUT:
- case VIDIOC_S_INPUT:
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_S_FREQUENCY:
- case VIDIOC_LOG_STATUS:
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- return bttv_common_ioctls(btv,cmd,arg);
+ mutex_lock(&fh->cap.lock);
- default:
- return -ENOIOCTLCMD;
+ if (fh->width < c.min_scaled_width) {
+ fh->width = c.min_scaled_width;
+ btv->init.width = c.min_scaled_width;
+ } else if (fh->width > c.max_scaled_width) {
+ fh->width = c.max_scaled_width;
+ btv->init.width = c.max_scaled_width;
+ }
+
+ if (fh->height < c.min_scaled_height) {
+ fh->height = c.min_scaled_height;
+ btv->init.height = c.min_scaled_height;
+ } else if (fh->height > c.max_scaled_height) {
+ fh->height = c.max_scaled_height;
+ btv->init.height = c.max_scaled_height;
}
- return 0;
- fh_unlock_and_return:
mutex_unlock(&fh->cap.lock);
- return retval;
- btv_unlock_and_return:
- mutex_unlock(&btv->lock);
- return retval;
+ return 0;
}
-static int bttv_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
{
- struct bttv_fh *fh = file->private_data;
-
- switch (cmd) {
- case BTTV_VBISIZE:
- {
- const struct bttv_tvnorm *tvnorm;
-
- tvnorm = fh->vbi_fmt.tvnorm;
-
- if (fh->vbi_fmt.fmt.start[0] != tvnorm->vbistart[0] ||
- fh->vbi_fmt.fmt.start[1] != tvnorm->vbistart[1] ||
- fh->vbi_fmt.fmt.count[0] != fh->vbi_fmt.fmt.count[1]) {
- /* BTTV_VBISIZE cannot express these parameters,
- however open() resets the paramters to defaults
- and apps shouldn't call BTTV_VBISIZE after
- VIDIOC_S_FMT. */
- return -EINVAL;
- }
-
- bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
- return (fh->vbi_fmt.fmt.count[0] * 2
- * fh->vbi_fmt.fmt.samples_per_line);
- }
+ strcpy(a->name, "audio");
+ return 0;
+}
- default:
- return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);
- }
+static int bttv_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ return 0;
}
static ssize_t bttv_read(struct file *file, char __user *data,
@@ -3719,8 +3205,8 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait)
}
poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == STATE_DONE ||
- buf->vb.state == STATE_ERROR)
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
@@ -3777,7 +3263,7 @@ static int bttv_open(struct inode *inode, struct file *file)
V4L2_FIELD_SEQ_TB,
sizeof(struct bttv_buffer),
fh);
- i2c_vidiocschan(btv);
+ set_tvnorm(btv,btv->tvnorm);
btv->users++;
@@ -3857,7 +3343,7 @@ static const struct file_operations bttv_fops =
.owner = THIS_MODULE,
.open = bttv_open,
.release = bttv_release,
- .ioctl = bttv_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
.read = bttv_read,
@@ -3867,19 +3353,61 @@ static const struct file_operations bttv_fops =
static struct video_device bttv_video_template =
{
- .name = "UNSET",
- .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|
- VID_TYPE_CLIPPING|VID_TYPE_SCALES,
- .fops = &bttv_fops,
- .minor = -1,
-};
-
-static struct video_device bttv_vbi_template =
-{
- .name = "bt848/878 vbi",
- .type = VID_TYPE_TUNER|VID_TYPE_TELETEXT,
.fops = &bttv_fops,
.minor = -1,
+ .vidioc_querycap = bttv_querycap,
+ .vidioc_enum_fmt_cap = bttv_enum_fmt_cap,
+ .vidioc_g_fmt_cap = bttv_g_fmt_cap,
+ .vidioc_try_fmt_cap = bttv_try_fmt_cap,
+ .vidioc_s_fmt_cap = bttv_s_fmt_cap,
+ .vidioc_enum_fmt_overlay = bttv_enum_fmt_overlay,
+ .vidioc_g_fmt_overlay = bttv_g_fmt_overlay,
+ .vidioc_try_fmt_overlay = bttv_try_fmt_overlay,
+ .vidioc_s_fmt_overlay = bttv_s_fmt_overlay,
+ .vidioc_enum_fmt_vbi = bttv_enum_fmt_vbi,
+ .vidioc_g_fmt_vbi = bttv_g_fmt_vbi,
+ .vidioc_try_fmt_vbi = bttv_try_fmt_vbi,
+ .vidioc_s_fmt_vbi = bttv_s_fmt_vbi,
+ .vidioc_g_audio = bttv_g_audio,
+ .vidioc_s_audio = bttv_s_audio,
+ .vidioc_cropcap = bttv_cropcap,
+ .vidioc_reqbufs = bttv_reqbufs,
+ .vidioc_querybuf = bttv_querybuf,
+ .vidioc_qbuf = bttv_qbuf,
+ .vidioc_dqbuf = bttv_dqbuf,
+ .vidioc_s_std = bttv_s_std,
+ .vidioc_enum_input = bttv_enum_input,
+ .vidioc_g_input = bttv_g_input,
+ .vidioc_s_input = bttv_s_input,
+ .vidioc_queryctrl = bttv_queryctrl,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_streamon = bttv_streamon,
+ .vidioc_streamoff = bttv_streamoff,
+ .vidioc_g_tuner = bttv_g_tuner,
+ .vidioc_s_tuner = bttv_s_tuner,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ .vidiocgmbuf = vidiocgmbuf,
+#endif
+ .vidioc_g_crop = bttv_g_crop,
+ .vidioc_g_crop = bttv_g_crop,
+ .vidioc_s_crop = bttv_s_crop,
+ .vidioc_g_fbuf = bttv_g_fbuf,
+ .vidioc_s_fbuf = bttv_s_fbuf,
+ .vidioc_overlay = bttv_overlay,
+ .vidioc_g_priority = bttv_g_priority,
+ .vidioc_s_priority = bttv_s_priority,
+ .vidioc_g_parm = bttv_g_parm,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
+ .vidioc_log_status = bttv_log_status,
+ .vidioc_querystd = bttv_querystd,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = bttv_g_register,
+ .vidioc_s_register = bttv_s_register,
+#endif
+ .tvnorms = BTTV_NORMS,
+ .current_norm = V4L2_STD_PAL,
};
/* ----------------------------------------------------------------------- */
@@ -3918,7 +3446,7 @@ static int radio_open(struct inode *inode, struct file *file)
static int radio_release(struct inode *inode, struct file *file)
{
- struct bttv *btv = file->private_data;
+ struct bttv *btv = file->private_data;
struct rds_command cmd;
btv->radio_user--;
@@ -3928,59 +3456,116 @@ static int radio_release(struct inode *inode, struct file *file)
return 0;
}
-static int radio_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
{
- struct bttv *btv = file->private_data;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- switch (cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability *cap = arg;
+ strcpy(cap->driver, "bttv");
+ strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
+ cap->version = BTTV_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
- memset(cap,0,sizeof(*cap));
- strcpy(cap->name,btv->radio_dev->name);
- cap->type = VID_TYPE_TUNER;
- cap->channels = 1;
- cap->audios = 1;
- return 0;
- }
+ return 0;
+}
- case VIDIOCGTUNER:
- {
- struct video_tuner *v = arg;
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- if(v->tuner)
- return -EINVAL;
- memset(v,0,sizeof(*v));
- strcpy(v->name, "Radio");
- bttv_call_i2c_clients(btv,cmd,v);
- return 0;
- }
- case VIDIOCSTUNER:
- /* nothing to do */
- return 0;
+ if (UNSET == bttv_tvcards[btv->c.type].tuner)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ mutex_lock(&btv->lock);
+ memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
- case BTTV_VERSION:
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- case VIDIOC_LOG_STATUS:
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- return bttv_common_ioctls(btv,cmd,arg);
+ bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+
+ mutex_unlock(&btv->lock);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctl;
- default:
- return -ENOIOCTLCMD;
- }
return 0;
}
-static int radio_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
{
- return video_usercopy(inode, file, cmd, arg, radio_do_ioctl);
+ *i = 0;
+ return 0;
}
static ssize_t radio_read(struct file *file, char __user *data,
@@ -4016,17 +3601,29 @@ static const struct file_operations radio_fops =
.open = radio_open,
.read = radio_read,
.release = radio_release,
- .ioctl = radio_ioctl,
+ .ioctl = video_ioctl2,
.llseek = no_llseek,
.poll = radio_poll,
};
static struct video_device radio_template =
{
- .name = "bt848/878 radio",
- .type = VID_TYPE_TUNER,
.fops = &radio_fops,
.minor = -1,
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
};
/* ----------------------------------------------------------------------- */
@@ -4308,20 +3905,20 @@ static void bttv_irq_timeout(unsigned long data)
bttv_set_dma(btv, 0);
/* wake up */
- bttv_irq_wakeup_video(btv, &old, &new, STATE_ERROR);
- bttv_irq_wakeup_vbi(btv, ovbi, STATE_ERROR);
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
+ bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
/* cancel all outstanding capture / vbi requests */
while (!list_empty(&btv->capture)) {
item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
list_del(&item->vb.queue);
- item->vb.state = STATE_ERROR;
+ item->vb.state = VIDEOBUF_ERROR;
wake_up(&item->vb.done);
}
while (!list_empty(&btv->vcapture)) {
item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
list_del(&item->vb.queue);
- item->vb.state = STATE_ERROR;
+ item->vb.state = VIDEOBUF_ERROR;
wake_up(&item->vb.done);
}
@@ -4344,7 +3941,7 @@ bttv_irq_wakeup_top(struct bttv *btv)
do_gettimeofday(&wakeup->vb.ts);
wakeup->vb.field_count = btv->field_count;
- wakeup->vb.state = STATE_DONE;
+ wakeup->vb.state = VIDEOBUF_DONE;
wake_up(&wakeup->vb.done);
spin_unlock(&btv->s_lock);
}
@@ -4393,7 +3990,7 @@ bttv_irq_switch_video(struct bttv *btv)
}
/* wake up finished buffers */
- bttv_irq_wakeup_video(btv, &old, &new, STATE_DONE);
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
spin_unlock(&btv->s_lock);
}
@@ -4426,7 +4023,7 @@ bttv_irq_switch_vbi(struct bttv *btv)
bttv_buffer_activate_vbi(btv, new);
bttv_set_dma(btv, 0);
- bttv_irq_wakeup_vbi(btv, old, STATE_DONE);
+ bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
spin_unlock(&btv->s_lock);
}
@@ -4548,8 +4145,9 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
/* initialitation */
static struct video_device *vdev_init(struct bttv *btv,
- struct video_device *template,
- char *type)
+ const struct video_device *template,
+ const char *type_name,
+ const int type)
{
struct video_device *vfd;
@@ -4560,9 +4158,10 @@ static struct video_device *vdev_init(struct bttv *btv,
vfd->minor = -1;
vfd->dev = &btv->c.pci->dev;
vfd->release = video_device_release;
+ vfd->type = type;
snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
- type, bttv_tvcards[btv->c.type].name);
+ type_name, bttv_tvcards[btv->c.type].name);
return vfd;
}
@@ -4594,6 +4193,11 @@ static void bttv_unregister_video(struct bttv *btv)
/* register video4linux devices */
static int __devinit bttv_register_video(struct bttv *btv)
{
+ int video_type = VID_TYPE_CAPTURE |
+ VID_TYPE_TUNER |
+ VID_TYPE_CLIPPING|
+ VID_TYPE_SCALES;
+
if (no_overlay <= 0) {
bttv_video_template.type |= VID_TYPE_OVERLAY;
} else {
@@ -4601,7 +4205,9 @@ static int __devinit bttv_register_video(struct bttv *btv)
}
/* video */
- btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+ btv->video_dev = vdev_init(btv, &bttv_video_template,
+ "video", video_type);
+
if (NULL == btv->video_dev)
goto err;
if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
@@ -4616,7 +4222,9 @@ static int __devinit bttv_register_video(struct bttv *btv)
}
/* vbi */
- btv->vbi_dev = vdev_init(btv, &bttv_vbi_template, "vbi");
+ btv->vbi_dev = vdev_init(btv, &bttv_video_template,
+ "vbi", VID_TYPE_TUNER | VID_TYPE_TELETEXT);
+
if (NULL == btv->vbi_dev)
goto err;
if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0)
@@ -4627,7 +4235,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (!btv->has_radio)
return 0;
/* radio */
- btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+ btv->radio_dev = vdev_init(btv, &radio_template,
+ "radio", VID_TYPE_TUNER);
if (NULL == btv->radio_dev)
goto err;
if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0)
@@ -4768,7 +4377,7 @@ static int __devinit bttv_probe(struct pci_dev *dev,
btv->init.btv = btv;
btv->init.ov.w.width = 320;
btv->init.ov.w.height = 240;
- btv->init.fmt = format_by_palette(VIDEO_PALETTE_RGB24);
+ btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
btv->init.width = 320;
btv->init.height = 240;
btv->input = 0;
@@ -5013,14 +4622,17 @@ static int __init bttv_init_module(void)
printk(KERN_WARNING "bttv: bus_register error: %d\n", ret);
return ret;
}
- return pci_register_driver(&bttv_pci_driver);
+ ret = pci_register_driver(&bttv_pci_driver);
+ if (ret < 0)
+ bus_unregister(&bttv_sub_bus_type);
+
+ return ret;
}
static void __exit bttv_cleanup_module(void)
{
pci_unregister_driver(&bttv_pci_driver);
bus_unregister(&bttv_sub_bus_type);
- return;
}
module_init(bttv_init_module);
diff --git a/trunk/drivers/media/video/bt8xx/bttv-input.c b/trunk/drivers/media/video/bt8xx/bttv-input.c
index e7c521b8444a..fc9ecb21eec6 100644
--- a/trunk/drivers/media/video/bt8xx/bttv-input.c
+++ b/trunk/drivers/media/video/bt8xx/bttv-input.c
@@ -69,6 +69,11 @@ static void ir_handle_key(struct bttv *btv)
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
ir_input_keydown(ir->dev,&ir->ir,data,data);
} else {
+ /* HACK: Probably, ir->mask_keydown is missing
+ for this board */
+ if (btv->c.type == BTTV_BOARD_WINFAST2000)
+ ir_input_keydown(ir->dev, &ir->ir, data, data);
+
ir_input_nokey(ir->dev,&ir->ir);
}
diff --git a/trunk/drivers/media/video/bt8xx/bttv-risc.c b/trunk/drivers/media/video/bt8xx/bttv-risc.c
index 58986f1a5f1a..e5979f77504c 100644
--- a/trunk/drivers/media/video/bt8xx/bttv-risc.c
+++ b/trunk/drivers/media/video/bt8xx/bttv-risc.c
@@ -582,7 +582,7 @@ bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf
videobuf_dma_free(dma);
btcx_riscmem_free(btv->c.pci,&buf->bottom);
btcx_riscmem_free(btv->c.pci,&buf->top);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
int
@@ -602,7 +602,7 @@ bttv_buffer_activate_vbi(struct bttv *btv,
if (vbi) {
unsigned int crop, vdelay;
- vbi->vb.state = STATE_ACTIVE;
+ vbi->vb.state = VIDEOBUF_ACTIVE;
list_del(&vbi->vb.queue);
/* VDELAY is start of video, end of VBI capturing. */
@@ -644,12 +644,12 @@ bttv_buffer_activate_video(struct bttv *btv,
/* video capture */
if (NULL != set->top && NULL != set->bottom) {
if (set->top == set->bottom) {
- set->top->vb.state = STATE_ACTIVE;
+ set->top->vb.state = VIDEOBUF_ACTIVE;
if (set->top->vb.queue.next)
list_del(&set->top->vb.queue);
} else {
- set->top->vb.state = STATE_ACTIVE;
- set->bottom->vb.state = STATE_ACTIVE;
+ set->top->vb.state = VIDEOBUF_ACTIVE;
+ set->bottom->vb.state = VIDEOBUF_ACTIVE;
if (set->top->vb.queue.next)
list_del(&set->top->vb.queue);
if (set->bottom->vb.queue.next)
@@ -666,7 +666,7 @@ bttv_buffer_activate_video(struct bttv *btv,
btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05),
~0x0f, BT848_COLOR_CTL);
} else if (NULL != set->top) {
- set->top->vb.state = STATE_ACTIVE;
+ set->top->vb.state = VIDEOBUF_ACTIVE;
if (set->top->vb.queue.next)
list_del(&set->top->vb.queue);
bttv_apply_geo(btv, &set->top->geo,1);
@@ -677,7 +677,7 @@ bttv_buffer_activate_video(struct bttv *btv,
btaor(set->top->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
btaor(set->top->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL);
} else if (NULL != set->bottom) {
- set->bottom->vb.state = STATE_ACTIVE;
+ set->bottom->vb.state = VIDEOBUF_ACTIVE;
if (set->bottom->vb.queue.next)
list_del(&set->bottom->vb.queue);
bttv_apply_geo(btv, &set->bottom->geo,1);
diff --git a/trunk/drivers/media/video/bt8xx/bttv-vbi.c b/trunk/drivers/media/video/bt8xx/bttv-vbi.c
index 346ce019bdcb..1f0cc79e2a33 100644
--- a/trunk/drivers/media/video/bt8xx/bttv-vbi.c
+++ b/trunk/drivers/media/video/bt8xx/bttv-vbi.c
@@ -142,7 +142,7 @@ static int vbi_buffer_prepare(struct videobuf_queue *q,
redo_dma_risc = 1;
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
redo_dma_risc = 1;
if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL)))
goto fail;
@@ -189,7 +189,7 @@ static int vbi_buffer_prepare(struct videobuf_queue *q,
/* For bttv_buffer_activate_vbi(). */
buf->geo.vdelay = min_vdelay;
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->vb.field = field;
dprintk("buf prepare %p: top=%p bottom=%p field=%s\n",
vb, &buf->top, &buf->bottom,
@@ -209,7 +209,7 @@ vbi_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
dprintk("queue %p\n",vb);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue,&btv->vcapture);
if (NULL == btv->cvbi) {
fh->btv->loop_irq |= 4;
@@ -236,10 +236,8 @@ struct videobuf_queue_ops bttv_vbi_qops = {
/* ----------------------------------------------------------------------- */
-static int
-try_fmt (struct v4l2_vbi_format * f,
- const struct bttv_tvnorm * tvnorm,
- __s32 crop_start)
+static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm,
+ __s32 crop_start)
{
__s32 min_start, max_start, max_end, f2_offset;
unsigned int i;
@@ -305,10 +303,9 @@ try_fmt (struct v4l2_vbi_format * f,
return 0;
}
-int
-bttv_vbi_try_fmt (struct bttv_fh * fh,
- struct v4l2_vbi_format * f)
+int bttv_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
{
+ struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
const struct bttv_tvnorm *tvnorm;
__s32 crop_start;
@@ -320,13 +317,13 @@ bttv_vbi_try_fmt (struct bttv_fh * fh,
mutex_unlock(&btv->lock);
- return try_fmt(f, tvnorm, crop_start);
+ return try_fmt(&frt->fmt.vbi, tvnorm, crop_start);
}
-int
-bttv_vbi_set_fmt (struct bttv_fh * fh,
- struct v4l2_vbi_format * f)
+
+int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
{
+ struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
const struct bttv_tvnorm *tvnorm;
__s32 start1, end;
@@ -340,11 +337,12 @@ bttv_vbi_set_fmt (struct bttv_fh * fh,
tvnorm = &bttv_tvnorms[btv->tvnorm];
- rc = try_fmt(f, tvnorm, btv->crop_start);
+ rc = try_fmt(&frt->fmt.vbi, tvnorm, btv->crop_start);
if (0 != rc)
goto fail;
- start1 = f->start[1] - tvnorm->vbistart[1] + tvnorm->vbistart[0];
+ start1 = frt->fmt.vbi.start[1] - tvnorm->vbistart[1] +
+ tvnorm->vbistart[0];
/* First possible line of video capturing. Should be
max(f->start[0] + f->count[0], start1 + f->count[1]) * 2
@@ -352,11 +350,11 @@ bttv_vbi_set_fmt (struct bttv_fh * fh,
pretend the VBI and video capture window may overlap,
so end = start + 1, the lowest possible value, times two
because vbi_fmt.end counts field lines times two. */
- end = max(f->start[0], start1) * 2 + 2;
+ end = max(frt->fmt.vbi.start[0], start1) * 2 + 2;
mutex_lock(&fh->vbi.lock);
- fh->vbi_fmt.fmt = *f;
+ fh->vbi_fmt.fmt = frt->fmt.vbi;
fh->vbi_fmt.tvnorm = tvnorm;
fh->vbi_fmt.end = end;
@@ -370,13 +368,13 @@ bttv_vbi_set_fmt (struct bttv_fh * fh,
return rc;
}
-void
-bttv_vbi_get_fmt (struct bttv_fh * fh,
- struct v4l2_vbi_format * f)
+
+int bttv_g_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
{
+ struct bttv_fh *fh = f;
const struct bttv_tvnorm *tvnorm;
- *f = fh->vbi_fmt.fmt;
+ frt->fmt.vbi = fh->vbi_fmt.fmt;
tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
@@ -391,28 +389,28 @@ bttv_vbi_get_fmt (struct bttv_fh * fh,
max_end = (tvnorm->cropcap.bounds.top
+ tvnorm->cropcap.bounds.height) >> 1;
- f->sampling_rate = tvnorm->Fsc;
+ frt->fmt.vbi.sampling_rate = tvnorm->Fsc;
for (i = 0; i < 2; ++i) {
__s32 new_start;
- new_start = f->start[i]
+ new_start = frt->fmt.vbi.start[i]
+ tvnorm->vbistart[i]
- fh->vbi_fmt.tvnorm->vbistart[i];
- f->start[i] = min(new_start, max_end - 1);
- f->count[i] = min((__s32) f->count[i],
- max_end - f->start[i]);
+ frt->fmt.vbi.start[i] = min(new_start, max_end - 1);
+ frt->fmt.vbi.count[i] =
+ min((__s32) frt->fmt.vbi.count[i],
+ max_end - frt->fmt.vbi.start[i]);
max_end += tvnorm->vbistart[1]
- tvnorm->vbistart[0];
}
}
+ return 0;
}
-void
-bttv_vbi_fmt_reset (struct bttv_vbi_fmt * f,
- int norm)
+void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm)
{
const struct bttv_tvnorm *tvnorm;
unsigned int real_samples_per_line;
diff --git a/trunk/drivers/media/video/bt8xx/bttv.h b/trunk/drivers/media/video/bt8xx/bttv.h
index 19e75d50a107..bf4c339a520c 100644
--- a/trunk/drivers/media/video/bt8xx/bttv.h
+++ b/trunk/drivers/media/video/bt8xx/bttv.h
@@ -241,7 +241,10 @@ struct tvcard
unsigned int radio_addr;
unsigned int has_radio;
- void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
+
+ void (*volume_gpio)(struct bttv *btv, __u16 volume);
+ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+
void (*muxsel_hook)(struct bttv *btv, unsigned int input);
};
diff --git a/trunk/drivers/media/video/bt8xx/bttvp.h b/trunk/drivers/media/video/bt8xx/bttvp.h
index d4ac4c4b49b4..1305d315cfc5 100644
--- a/trunk/drivers/media/video/bt8xx/bttvp.h
+++ b/trunk/drivers/media/video/bt8xx/bttvp.h
@@ -84,6 +84,11 @@
#define clamp(x, low, high) min (max (low, x), high)
+#define BTTV_NORMS (\
+ V4L2_STD_PAL | V4L2_STD_PAL_N | \
+ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60)
/* ---------------------------------------------------------- */
struct bttv_tvnorm {
@@ -112,7 +117,6 @@ extern const struct bttv_tvnorm bttv_tvnorms[];
struct bttv_format {
char *name;
- int palette; /* video4linux 1 */
int fourcc; /* video4linux 2 */
int btformat; /* BT848_COLOR_FMT_* */
int btswap; /* BT848_COLOR_CTL_* */
@@ -253,9 +257,9 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
/* ---------------------------------------------------------- */
/* bttv-vbi.c */
-int bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
-void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
-int bttv_vbi_set_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
+int bttv_try_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_g_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_s_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
extern struct videobuf_queue_ops bttv_vbi_qops;
@@ -337,7 +341,9 @@ struct bttv {
/* old gpio interface */
wait_queue_head_t gpioq;
int shutdown;
- void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
+
+ void (*volume_gpio)(struct bttv *btv, __u16 volume);
+ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set);
/* new gpio interface */
spinlock_t gpio_lock;
@@ -458,10 +464,6 @@ struct bttv {
extern unsigned int bttv_num;
extern struct bttv bttvs[BTTV_MAX];
-/* private ioctls */
-#define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
-#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
-
#endif
#define btwrite(dat,adr) writel((dat), btv->bt848_mmio+(adr))
diff --git a/trunk/drivers/media/video/bw-qcam.c b/trunk/drivers/media/video/bw-qcam.c
index 58423525591f..032265383df2 100644
--- a/trunk/drivers/media/video/bw-qcam.c
+++ b/trunk/drivers/media/video/bw-qcam.c
@@ -82,11 +82,16 @@ OTHER DEALINGS IN THE SOFTWARE.
static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */
static unsigned int yieldlines=4; /* Yield after this many during capture */
static int video_nr = -1;
+static unsigned int force_init; /* Whether to probe aggressively */
module_param(maxpoll, int, 0);
module_param(yieldlines, int, 0);
module_param(video_nr, int, 0);
+/* Set force_init=1 to avoid detection by polling status register and
+ * immediately attempt to initialize qcam */
+module_param(force_init, int, 0);
+
static inline int read_lpstatus(struct qcam_device *q)
{
return parport_read_status(q->pport);
@@ -331,6 +336,9 @@ static int qc_detect(struct qcam_device *q)
int count = 0;
int i;
+ if (force_init)
+ return 1;
+
lastreg = reg = read_lpstatus(q) & 0xf0;
for (i = 0; i < 500; i++)
@@ -354,12 +362,12 @@ static int qc_detect(struct qcam_device *q)
/* Be (even more) liberal in what you accept... */
-/* if (count > 30 && count < 200) */
if (count > 20 && count < 400) {
return 1; /* found */
} else {
printk(KERN_ERR "No Quickcam found on port %s\n",
q->pport->name);
+ printk(KERN_DEBUG "Quickcam detection counter: %u\n", count);
return 0; /* not found */
}
}
diff --git a/trunk/drivers/media/video/cs5345.c b/trunk/drivers/media/video/cs5345.c
new file mode 100644
index 000000000000..fae469ce16f5
--- /dev/null
+++ b/trunk/drivers/media/video/cs5345.c
@@ -0,0 +1,168 @@
+/*
+ * cs5345 Cirrus Logic 24-bit, 192 kHz Stereo Audio ADC
+ * Copyright (C) 2007 Hans Verkuil
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug;
+
+module_param(debug, bool, 0644);
+
+MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On");
+
+
+/* ----------------------------------------------------------------------- */
+
+static inline int cs5345_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int cs5345_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ struct v4l2_routing *route = arg;
+ struct v4l2_control *ctrl = arg;
+
+ switch (cmd) {
+ case VIDIOC_INT_G_AUDIO_ROUTING:
+ route->input = cs5345_read(client, 0x09) & 7;
+ route->input |= cs5345_read(client, 0x05) & 0x70;
+ route->output = 0;
+ break;
+
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ if ((route->input & 0xf) > 6) {
+ v4l_err(client, "Invalid input %d.\n", route->input);
+ return -EINVAL;
+ }
+ cs5345_write(client, 0x09, route->input & 0xf);
+ cs5345_write(client, 0x05, route->input & 0xf0);
+ break;
+
+ case VIDIOC_G_CTRL:
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl->value = (cs5345_read(client, 0x04) & 0x08) != 0;
+ break;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ ctrl->value = cs5345_read(client, 0x07) & 0x3f;
+ if (ctrl->value >= 32)
+ ctrl->value = ctrl->value - 64;
+ break;
+
+ case VIDIOC_S_CTRL:
+ break;
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ cs5345_write(client, 0x04, ctrl->value ? 0x80 : 0);
+ break;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ if (ctrl->value > 24 || ctrl->value < -24)
+ return -EINVAL;
+ cs5345_write(client, 0x07, ((u8)ctrl->value) & 0x3f);
+ cs5345_write(client, 0x08, ((u8)ctrl->value) & 0x3f);
+ break;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_S_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (cmd == VIDIOC_DBG_G_REGISTER)
+ reg->val = cs5345_read(client, reg->reg & 0x1f);
+ else
+ cs5345_write(client, reg->reg & 0x1f, reg->val & 0x1f);
+ break;
+ }
+#endif
+
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client,
+ arg, V4L2_IDENT_CS5345, 0);
+
+ case VIDIOC_LOG_STATUS:
+ {
+ u8 v = cs5345_read(client, 0x09) & 7;
+ u8 m = cs5345_read(client, 0x04);
+ int vol = cs5345_read(client, 0x08) & 0x3f;
+
+ v4l_info(client, "Input: %d%s\n", v,
+ (m & 0x80) ? " (muted)" : "");
+ if (vol >= 32)
+ vol = vol - 64;
+ v4l_info(client, "Volume: %d dB\n", vol);
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cs5345_probe(struct i2c_client *client)
+{
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ cs5345_write(client, 0x02, 0x00);
+ cs5345_write(client, 0x04, 0x01);
+ cs5345_write(client, 0x09, 0x01);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cs5345",
+ .driverid = I2C_DRIVERID_CS5345,
+ .command = cs5345_command,
+ .probe = cs5345_probe,
+};
+
diff --git a/trunk/drivers/media/video/cs53l32a.c b/trunk/drivers/media/video/cs53l32a.c
index a73e285af730..f41bfde045fe 100644
--- a/trunk/drivers/media/video/cs53l32a.c
+++ b/trunk/drivers/media/video/cs53l32a.c
@@ -29,12 +29,13 @@
#include
#include
#include
+#include
MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
MODULE_AUTHOR("Martin Vaughan");
MODULE_LICENSE("GPL");
-static int debug = 0;
+static int debug;
module_param(debug, bool, 0644);
@@ -57,8 +58,7 @@ static int cs53l32a_read(struct i2c_client *client, u8 reg)
return i2c_smbus_read_byte_data(client, reg);
}
-static int cs53l32a_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct v4l2_routing *route = arg;
struct v4l2_control *ctrl = arg;
@@ -105,7 +105,8 @@ static int cs53l32a_command(struct i2c_client *client, unsigned int cmd,
break;
case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_CS53l32A, 0);
+ return v4l2_chip_ident_i2c_client(client,
+ arg, V4L2_IDENT_CS53l32A, 0);
case VIDIOC_LOG_STATUS:
{
@@ -134,27 +135,18 @@ static int cs53l32a_command(struct i2c_client *client, unsigned int cmd,
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
-static struct i2c_driver i2c_driver;
-
-static int cs53l32a_attach(struct i2c_adapter *adapter, int address, int kind)
+static int cs53l32a_probe(struct i2c_client *client)
{
- struct i2c_client *client;
int i;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
snprintf(client->name, sizeof(client->name) - 1, "cs53l32a");
- v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
for (i = 1; i <= 7; i++) {
u8 v = cs53l32a_read(client, i);
@@ -179,55 +171,13 @@ static int cs53l32a_attach(struct i2c_adapter *adapter, int address, int kind)
v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v);
}
-
- i2c_attach_client(client);
-
return 0;
}
-static int cs53l32a_probe(struct i2c_adapter *adapter)
-{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, cs53l32a_attach);
- return 0;
-}
-
-static int cs53l32a_detach(struct i2c_client *client)
-{
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
- kfree(client);
-
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .driver = {
- .name = "cs53l32a",
- },
- .id = I2C_DRIVERID_CS53L32A,
- .attach_adapter = cs53l32a_probe,
- .detach_client = cs53l32a_detach,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cs53l32a",
+ .driverid = I2C_DRIVERID_CS53L32A,
.command = cs53l32a_command,
+ .probe = cs53l32a_probe,
};
-
-static int __init cs53l32a_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit cs53l32a_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(cs53l32a_init_module);
-module_exit(cs53l32a_cleanup_module);
diff --git a/trunk/drivers/media/video/cx2341x.c b/trunk/drivers/media/video/cx2341x.c
index 62304255dcae..c592899a2317 100644
--- a/trunk/drivers/media/video/cx2341x.c
+++ b/trunk/drivers/media/video/cx2341x.c
@@ -34,7 +34,7 @@ MODULE_DESCRIPTION("cx23415/6 driver");
MODULE_AUTHOR("Hans Verkuil");
MODULE_LICENSE("GPL");
-static int debug = 0;
+static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
@@ -75,6 +75,7 @@ const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
0
};
+EXPORT_SYMBOL(cx2341x_mpeg_ctrls);
/* Map the control ID to the correct field in the cx2341x_mpeg_params
@@ -281,13 +282,14 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy,
return -EBUSY;
params->stream_type = ctrl->value;
params->video_encoding =
- (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
- params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
- V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
- if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+ (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+ params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
/* MPEG-1 implies CBR */
- params->video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
- }
+ params->video_bitrate_mode =
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
break;
case V4L2_CID_MPEG_STREAM_VBI_FMT:
params->stream_vbi_fmt = ctrl->value;
@@ -334,7 +336,8 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy,
return 0;
}
-static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
+static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
+ s32 min, s32 max, s32 step, s32 def)
{
const char *name;
@@ -417,7 +420,8 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 ma
return 0;
}
-int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl *qctrl)
+int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
+ struct v4l2_queryctrl *qctrl)
{
int err;
@@ -440,7 +444,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
err = v4l2_ctrl_query_fill_std(qctrl);
- if (err == 0 && params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ if (err == 0 &&
+ params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return err;
@@ -455,13 +460,16 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
err = v4l2_ctrl_query_fill_std(qctrl);
- if (err == 0 && params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ if (err == 0 &&
+ params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return err;
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
err = v4l2_ctrl_query_fill_std(qctrl);
- if (err == 0 && params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ if (err == 0 &&
+ params->video_bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return err;
@@ -476,80 +484,90 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
/* CX23415/6 specific */
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
return cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
- V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1,
- V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, 0);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, 1,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF);
- if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+ 1,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF);
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
- V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, 1,
- V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF);
- if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ 1,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF);
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
return cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
- V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1,
- V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, 0);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_temporal_filter_mode == V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_temporal_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
return cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 255);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 0);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 255);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 0);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
@@ -560,6 +578,7 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
}
}
+EXPORT_SYMBOL(cx2341x_ctrl_query);
const char **cx2341x_ctrl_get_menu(u32 id)
{
@@ -629,6 +648,7 @@ const char **cx2341x_ctrl_get_menu(u32 id)
return v4l2_ctrl_get_menu(id);
}
}
+EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
{
@@ -637,9 +657,8 @@ static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
((1 + params->audio_l2_bitrate) << 4) |
(params->audio_mode << 8) |
(params->audio_mode_extension << 10) |
- (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) ?
- 3 :
- params->audio_emphasis) << 12) |
+ (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+ ? 3 : params->audio_emphasis) << 12) |
(params->audio_crc << 14);
}
@@ -679,19 +698,19 @@ int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
if (err)
break;
}
- if (err == 0 && params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
- params->video_bitrate_peak < params->video_bitrate) {
+ if (err == 0 &&
+ params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ params->video_bitrate_peak < params->video_bitrate) {
err = -ERANGE;
ctrls->error_idx = ctrls->count;
}
- if (err) {
+ if (err)
ctrls->error_idx = i;
- }
- else {
+ else
cx2341x_calc_audio_properties(params);
- }
return err;
}
+EXPORT_SYMBOL(cx2341x_ext_ctrls);
void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
{
@@ -732,13 +751,18 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
.video_mute_yuv = 0x008080, /* YCbCr value for black */
/* encoding filters */
- .video_spatial_filter_mode = V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ .video_spatial_filter_mode =
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
.video_spatial_filter = 0,
- .video_luma_spatial_filter_type = V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
- .video_chroma_spatial_filter_type = V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
- .video_temporal_filter_mode = V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ .video_luma_spatial_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ .video_chroma_spatial_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ .video_temporal_filter_mode =
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
.video_temporal_filter = 8,
- .video_median_filter_type = V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ .video_median_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
.video_luma_median_filter_top = 255,
.video_luma_median_filter_bottom = 0,
.video_chroma_median_filter_top = 255,
@@ -748,8 +772,10 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
*p = default_params;
cx2341x_calc_audio_properties(p);
}
+EXPORT_SYMBOL(cx2341x_fill_defaults);
-static int cx2341x_api(void *priv, cx2341x_mbox_func func, int cmd, int args, ...)
+static int cx2341x_api(void *priv, cx2341x_mbox_func func,
+ u32 cmd, int args, ...)
{
u32 data[CX2341X_MBOX_MAX_DATA];
va_list vargs;
@@ -757,15 +783,17 @@ static int cx2341x_api(void *priv, cx2341x_mbox_func func, int cmd, int args, ..
va_start(vargs, args);
- for (i = 0; i < args; i++) {
+ for (i = 0; i < args; i++)
data[i] = va_arg(vargs, int);
- }
va_end(vargs);
return func(priv, cmd, args, 0, data);
}
+#define NEQ(field) (old->field != new->field)
+
int cx2341x_update(void *priv, cx2341x_mbox_func func,
- const struct cx2341x_mpeg_params *old, const struct cx2341x_mpeg_params *new)
+ const struct cx2341x_mpeg_params *old,
+ const struct cx2341x_mpeg_params *new)
{
static int mpeg_stream_type[] = {
0, /* MPEG-2 PS */
@@ -777,17 +805,18 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
};
int err = 0;
+ int force = (old == NULL);
u16 temporal = new->video_temporal_filter;
cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0);
- if (old == NULL || old->is_50hz != new->is_50hz) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1, new->is_50hz);
+ if (force || NEQ(is_50hz)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1,
+ new->is_50hz);
if (err) return err;
}
- if (old == NULL || old->width != new->width || old->height != new->height ||
- old->video_encoding != new->video_encoding) {
+ if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) {
u16 w = new->width;
u16 h = new->height;
@@ -795,69 +824,74 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
w /= 2;
h /= 2;
}
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2,
+ h, w);
if (err) return err;
}
if (new->width != 720 || new->height != (new->is_50hz ? 576 : 480)) {
- /* Adjust temporal filter if necessary. The problem with the temporal
- filter is that it works well with full resolution capturing, but
- not when the capture window is scaled (the filter introduces
- a ghosting effect). So if the capture window is scaled, then
- force the filter to 0.
+ /* Adjust temporal filter if necessary. The problem with the
+ temporal filter is that it works well with full resolution
+ capturing, but not when the capture window is scaled (the
+ filter introduces a ghosting effect). So if the capture
+ window is scaled, then force the filter to 0.
For full resolution the filter really improves the video
- quality, especially if the original video quality is suboptimal. */
+ quality, especially if the original video quality is
+ suboptimal. */
temporal = 0;
}
- if (old == NULL || old->stream_type != new->stream_type) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[new->stream_type]);
+ if (force || NEQ(stream_type)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1,
+ mpeg_stream_type[new->stream_type]);
if (err) return err;
}
- if (old == NULL || old->video_aspect != new->video_aspect) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1, 1 + new->video_aspect);
+ if (force || NEQ(video_aspect)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1,
+ 1 + new->video_aspect);
if (err) return err;
}
- if (old == NULL || old->video_b_frames != new->video_b_frames ||
- old->video_gop_size != new->video_gop_size) {
+ if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
new->video_gop_size, new->video_b_frames + 1);
if (err) return err;
}
- if (old == NULL || old->video_gop_closure != new->video_gop_closure) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, new->video_gop_closure);
+ if (force || NEQ(video_gop_closure)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1,
+ new->video_gop_closure);
if (err) return err;
}
- if (old == NULL || old->audio_properties != new->audio_properties) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties);
+ if (force || NEQ(audio_properties)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES,
+ 1, new->audio_properties);
if (err) return err;
}
- if (old == NULL || old->audio_mute != new->audio_mute) {
- err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, new->audio_mute);
+ if (force || NEQ(audio_mute)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1,
+ new->audio_mute);
if (err) return err;
}
- if (old == NULL || old->video_bitrate_mode != new->video_bitrate_mode ||
- old->video_bitrate != new->video_bitrate ||
- old->video_bitrate_peak != new->video_bitrate_peak) {
+ if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) ||
+ NEQ(video_bitrate_peak)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5,
new->video_bitrate_mode, new->video_bitrate,
new->video_bitrate_peak / 400, 0, 0);
if (err) return err;
}
- if (old == NULL || old->video_spatial_filter_mode != new->video_spatial_filter_mode ||
- old->video_temporal_filter_mode != new->video_temporal_filter_mode ||
- old->video_median_filter_type != new->video_median_filter_type) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
- new->video_spatial_filter_mode | (new->video_temporal_filter_mode << 1),
+ if (force || NEQ(video_spatial_filter_mode) ||
+ NEQ(video_temporal_filter_mode) ||
+ NEQ(video_median_filter_type)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE,
+ 2, new->video_spatial_filter_mode |
+ (new->video_temporal_filter_mode << 1),
new->video_median_filter_type);
if (err) return err;
}
- if (old == NULL ||
- old->video_luma_median_filter_bottom != new->video_luma_median_filter_bottom ||
- old->video_luma_median_filter_top != new->video_luma_median_filter_top ||
- old->video_chroma_median_filter_bottom != new->video_chroma_median_filter_bottom ||
- old->video_chroma_median_filter_top != new->video_chroma_median_filter_top) {
+ if (force || NEQ(video_luma_median_filter_bottom) ||
+ NEQ(video_luma_median_filter_top) ||
+ NEQ(video_chroma_median_filter_bottom) ||
+ NEQ(video_chroma_median_filter_top)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4,
new->video_luma_median_filter_bottom,
new->video_luma_median_filter_top,
@@ -865,36 +899,39 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
new->video_chroma_median_filter_top);
if (err) return err;
}
- if (old == NULL ||
- old->video_luma_spatial_filter_type != new->video_luma_spatial_filter_type ||
- old->video_chroma_spatial_filter_type != new->video_chroma_spatial_filter_type) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
- new->video_luma_spatial_filter_type, new->video_chroma_spatial_filter_type);
+ if (force || NEQ(video_luma_spatial_filter_type) ||
+ NEQ(video_chroma_spatial_filter_type)) {
+ err = cx2341x_api(priv, func,
+ CX2341X_ENC_SET_SPATIAL_FILTER_TYPE,
+ 2, new->video_luma_spatial_filter_type,
+ new->video_chroma_spatial_filter_type);
if (err) return err;
}
- if (old == NULL ||
- old->video_spatial_filter != new->video_spatial_filter ||
- old->video_temporal_filter != temporal) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
- new->video_spatial_filter, temporal);
+ if (force || NEQ(video_spatial_filter) ||
+ old->video_temporal_filter != temporal) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS,
+ 2, new->video_spatial_filter, temporal);
if (err) return err;
}
- if (old == NULL || old->video_temporal_decimation != new->video_temporal_decimation) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE, 1,
- new->video_temporal_decimation);
+ if (force || NEQ(video_temporal_decimation)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE,
+ 1, new->video_temporal_decimation);
if (err) return err;
}
- if (old == NULL || old->video_mute != new->video_mute ||
- (new->video_mute && old->video_mute_yuv != new->video_mute_yuv)) {
- err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, new->video_mute | (new->video_mute_yuv << 8));
+ if (force || NEQ(video_mute) ||
+ (new->video_mute && NEQ(video_mute_yuv))) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1,
+ new->video_mute | (new->video_mute_yuv << 8));
if (err) return err;
}
- if (old == NULL || old->stream_insert_nav_packets != new->stream_insert_nav_packets) {
- err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, 7, new->stream_insert_nav_packets);
+ if (force || NEQ(stream_insert_nav_packets)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2,
+ 7, new->stream_insert_nav_packets);
if (err) return err;
}
return 0;
}
+EXPORT_SYMBOL(cx2341x_update);
static const char *cx2341x_menu_item(struct cx2341x_mpeg_params *p, u32 id)
{
@@ -943,18 +980,17 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT),
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE),
p->video_bitrate);
- if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) {
+ if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
printk(", Peak %d", p->video_bitrate_peak);
- }
printk("\n");
- printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n",
+ printk(KERN_INFO
+ "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n",
prefix,
p->video_gop_size, p->video_b_frames,
p->video_gop_closure ? "" : "No ");
- if (p->video_temporal_decimation) {
+ if (p->video_temporal_decimation)
printk(KERN_INFO "%s: Video: Temporal Decimation %d\n",
prefix, p->video_temporal_decimation);
- }
/* Audio */
printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s",
@@ -964,10 +1000,9 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE),
p->audio_mute ? " (muted)" : "");
- if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) {
- printk(", %s",
- cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
- }
+ if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ printk(", %s", cx2341x_menu_item(p,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
printk(", %s, %s\n",
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC));
@@ -975,33 +1010,33 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
/* Encoding filters */
printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n",
prefix,
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE),
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE),
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE),
p->video_spatial_filter);
- if (p->width != 720 || p->height != (p->is_50hz ? 576 : 480)) {
+
+ if (p->width != 720 || p->height != (p->is_50hz ? 576 : 480))
temporal = 0;
- }
+
printk(KERN_INFO "%s: Temporal Filter: %s, %d\n",
prefix,
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE),
temporal);
- printk(KERN_INFO "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
+ printk(KERN_INFO
+ "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
prefix,
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE),
p->video_luma_median_filter_bottom,
p->video_luma_median_filter_top,
p->video_chroma_median_filter_bottom,
p->video_chroma_median_filter_top);
}
-
-EXPORT_SYMBOL(cx2341x_fill_defaults);
-EXPORT_SYMBOL(cx2341x_ctrl_query);
-EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
-EXPORT_SYMBOL(cx2341x_ext_ctrls);
-EXPORT_SYMBOL(cx2341x_update);
EXPORT_SYMBOL(cx2341x_log_status);
-EXPORT_SYMBOL(cx2341x_mpeg_ctrls);
/*
* Local variables:
diff --git a/trunk/drivers/media/video/cx23885/Kconfig b/trunk/drivers/media/video/cx23885/Kconfig
index 081ee6e1536f..1fd326fe4113 100644
--- a/trunk/drivers/media/video/cx23885/Kconfig
+++ b/trunk/drivers/media/video/cx23885/Kconfig
@@ -12,6 +12,10 @@ config VIDEO_CX23885
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
+ select TUNER_XC2028 if !DVB_FE_CUSTOMIZE
+ select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
+ select DVB_TDA18271 if !DVB_FE_CUSTOMIZE
+ select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
---help---
This is a video4linux driver for Conexant 23885 based
TV cards.
diff --git a/trunk/drivers/media/video/cx23885/Makefile b/trunk/drivers/media/video/cx23885/Makefile
index 665067022d2a..32c90be50602 100644
--- a/trunk/drivers/media/video/cx23885/Makefile
+++ b/trunk/drivers/media/video/cx23885/Makefile
@@ -1,4 +1,4 @@
-cx23885-objs := cx23885-cards.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o
+cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
diff --git a/trunk/drivers/media/video/cx23885/cx23885-cards.c b/trunk/drivers/media/video/cx23885/cx23885-cards.c
index b9012acabb2f..2d414dad5c31 100644
--- a/trunk/drivers/media/video/cx23885/cx23885-cards.c
+++ b/trunk/drivers/media/video/cx23885/cx23885-cards.c
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include "cx23885.h"
@@ -32,6 +33,8 @@
struct cx23885_board cx23885_boards[] = {
[CX23885_BOARD_UNKNOWN] = {
.name = "UNKNOWN/GENERIC",
+ /* Ensure safe default for unknown boards */
+ .clk_freq = 0,
.input = {{
.type = CX23885_VMUX_COMPOSITE1,
.vmux = 0,
@@ -69,23 +72,29 @@ struct cx23885_board cx23885_boards[] = {
},
[CX23885_BOARD_HAUPPAUGE_HVR1800] = {
.name = "Hauppauge WinTV-HVR1800",
+ .porta = CX23885_ANALOG_VIDEO,
.portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
.input = {{
.type = CX23885_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xff00,
- },{
- .type = CX23885_VMUX_DEBUG,
- .vmux = 0,
- .gpio0 = 0xff01,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .gpio0 = 0,
},{
.type = CX23885_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xff02,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .gpio0 = 0,
},{
.type = CX23885_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xff02,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .gpio0 = 0,
}},
},
[CX23885_BOARD_HAUPPAUGE_HVR1250] = {
@@ -113,6 +122,14 @@ struct cx23885_board cx23885_boards[] = {
.name = "DViCO FusionHDTV5 Express",
.portb = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_HAUPPAUGE_HVR1500Q] = {
+ .name = "Hauppauge WinTV-HVR1500Q",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1500] = {
+ .name = "Hauppauge WinTV-HVR1500",
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -136,6 +153,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x0070,
.subdevice = 0x7801,
.card = CX23885_BOARD_HAUPPAUGE_HVR1800,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7809,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
},{
.subvendor = 0x0070,
.subdevice = 0x7911,
@@ -144,6 +165,22 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x18ac,
.subdevice = 0xd500,
.card = CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7790,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7797,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7710,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7717,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -184,9 +221,19 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
switch (tv.model)
{
case 76601: /* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual channel ATSC and MPEG2 HW Encoder */
- case 77001: /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC and Basic analog */
- case 78501: /* WinTV-HVR1800 (PCIe, Retail, IR, Dual channel ATSC and MPEG2 HW Encoder */
- case 78521: /* WinTV-HVR1800 (PCIe, Retail, IR, Dual channel ATSC and MPEG2 HW Encoder */
+ case 77001: /* WinTV-HVR1500 (Express Card, OEM, No IR, ATSC and Basic analog */
+ case 77011: /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC and Basic analog */
+ case 77041: /* WinTV-HVR1500Q (Express Card, OEM, No IR, ATSC/QAM and Basic analog */
+ case 77051: /* WinTV-HVR1500Q (Express Card, Retail, No IR, ATSC/QAM and Basic analog */
+ case 78011: /* WinTV-HVR1800 (PCIe, Retail, 3.5mm in, IR, No FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 78501: /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 78521: /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 78531: /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, No FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 78631: /* WinTV-HVR1800 (PCIe, OEM, No IR, No FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 79001: /* WinTV-HVR1250 (PCIe, Retail, IR, full height, ATSC and Basic analog */
+ case 79101: /* WinTV-HVR1250 (PCIe, Retail, IR, half height, ATSC and Basic analog */
+ case 79571: /* WinTV-HVR1250 (PCIe, OEM, No IR, full height, ATSC and Basic analog */
+ case 79671: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */
break;
default:
printk("%s: warning: unknown hauppauge model #%d\n", dev->name, tv.model);
@@ -197,6 +244,34 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
dev->name, tv.model);
}
+/* Tuner callback function for cx23885 boards. Currently only needed
+ * for HVR1500Q, which has an xc5000 tuner.
+ */
+int cx23885_tuner_callback(void *priv, int command, int arg)
+{
+ struct cx23885_i2c *bus = priv;
+ struct cx23885_dev *dev = bus->dev;
+
+ switch(dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ if(command == 0) { /* Tuner Reset Command from xc5000 */
+ /* Drive the tuner into reset and out */
+ cx_clear(GP0_IO, 0x00000004);
+ mdelay(200);
+ cx_set(GP0_IO, 0x00000004);
+ return 0;
+ }
+ else {
+ printk(KERN_ERR
+ "%s(): Unknow command.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ return 0; /* Should never be here */
+}
+
void cx23885_gpio_setup(struct cx23885_dev *dev)
{
switch(dev->board) {
@@ -204,6 +279,23 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
/* GPIO-0 cx24227 demodulator reset */
cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ /* GPIO-0 cx24227 demodulator */
+ /* GPIO-2 xc3028 tuner */
+
+ /* Put the parts into reset */
+ cx_set(GP0_IO, 0x00050000);
+ cx_clear(GP0_IO, 0x00000005);
+ msleep(5);
+
+ /* Bring the parts out of reset */
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ /* GPIO-0 cx24227 demodulator reset */
+ /* GPIO-2 xc5000 tuner reset */
+ cx_set(GP0_IO, 0x00050005); /* Bring the part out of reset */
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1800:
/* GPIO-0 656_CLK */
/* GPIO-1 656_D0 */
@@ -212,7 +304,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
/* GPIO-11-14 cx23417 addr0-3 */
/* GPIO-15-18 cx23417 READY, CS, RD, WR */
/* GPIO-19 IR_RX */
- // FIXME: Analog requires the tuner is brought out of reset
+
+ /* Force the TDA8295A into reset and back */
+ cx_set(GP0_IO, 0x00040004);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000004);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00040004);
+ mdelay(20);
break;
}
}
@@ -221,6 +320,8 @@ int cx23885_ir_init(struct cx23885_dev *dev)
{
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
/* FIXME: Implement me */
break;
@@ -244,6 +345,8 @@ void cx23885_card_setup(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
if (dev->i2c_bus[0].i2c_rc == 0)
@@ -258,6 +361,8 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
default:
@@ -270,8 +375,6 @@ void cx23885_card_setup(struct cx23885_dev *dev)
/* ------------------------------------------------------------------ */
-EXPORT_SYMBOL(cx23885_boards);
-
/*
* Local variables:
* c-basic-offset: 8
diff --git a/trunk/drivers/media/video/cx23885/cx23885-core.c b/trunk/drivers/media/video/cx23885/cx23885-core.c
index 3cdd136477e5..8e40c7bcc06d 100644
--- a/trunk/drivers/media/video/cx23885/cx23885-core.c
+++ b/trunk/drivers/media/video/cx23885/cx23885-core.c
@@ -36,7 +36,7 @@ MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth ");
MODULE_LICENSE("GPL");
-static unsigned int debug = 0;
+static unsigned int debug;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages");
@@ -44,13 +44,15 @@ static unsigned int card[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card,"card type");
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/0: " fmt, dev->name , ## arg)
+#define dprintk(level, fmt, arg...)\
+ do { if (debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
static unsigned int cx23885_devcount;
static DEFINE_MUTEX(devlist);
-static LIST_HEAD(cx23885_devlist);
+LIST_HEAD(cx23885_devlist);
#define NO_SYNC_LINE (-1U)
@@ -73,14 +75,14 @@ static LIST_HEAD(cx23885_devlist);
* 0x00010ea0 0x00010xxx Free
*/
-struct sram_channel cx23885_sram_channels[] = {
+static struct sram_channel cx23885_sram_channels[] = {
[SRAM_CH01] = {
- .name = "test ch1",
+ .name = "VID A",
.cmds_start = 0x10000,
- .ctrl_start = 0x10500,
- .cdt = 0x10900,
- .fifo_start = 0x3000,
- .fifo_size = 0x1000,
+ .ctrl_start = 0x105b0,
+ .cdt = 0x107b0,
+ .fifo_start = 0x40,
+ .fifo_size = 0x2800,
.ptr1_reg = DMA1_PTR1,
.ptr2_reg = DMA1_PTR2,
.cnt1_reg = DMA1_CNT1,
@@ -102,8 +104,8 @@ struct sram_channel cx23885_sram_channels[] = {
[SRAM_CH03] = {
.name = "TS1 B",
.cmds_start = 0x100A0,
- .ctrl_start = 0x10780,
- .cdt = 0x10400,
+ .ctrl_start = 0x10630,
+ .cdt = 0x10870,
.fifo_start = 0x5000,
.fifo_size = 0x1000,
.ptr1_reg = DMA3_PTR1,
@@ -139,7 +141,7 @@ struct sram_channel cx23885_sram_channels[] = {
.name = "TS2 C",
.cmds_start = 0x10140,
.ctrl_start = 0x10680,
- .cdt = 0x10480,
+ .cdt = 0x108d0,
.fifo_start = 0x6000,
.fifo_size = 0x1000,
.ptr1_reg = DMA5_PTR1,
@@ -205,14 +207,14 @@ struct sram_channel cx23885_sram_channels[] = {
* 0x00010ea0 0x00010xxx Free
*/
-struct sram_channel cx23887_sram_channels[] = {
+static struct sram_channel cx23887_sram_channels[] = {
[SRAM_CH01] = {
- .name = "test ch1",
- .cmds_start = 0x0,
- .ctrl_start = 0x0,
- .cdt = 0x0,
- .fifo_start = 0x0,
- .fifo_size = 0x0,
+ .name = "VID A",
+ .cmds_start = 0x10000,
+ .ctrl_start = 0x105b0,
+ .cdt = 0x107b0,
+ .fifo_start = 0x40,
+ .fifo_size = 0x2800,
.ptr1_reg = DMA1_PTR1,
.ptr2_reg = DMA1_PTR2,
.cnt1_reg = DMA1_CNT1,
@@ -231,12 +233,12 @@ struct sram_channel cx23887_sram_channels[] = {
.cnt2_reg = DMA2_CNT2,
},
[SRAM_CH03] = {
- .name = "ch3",
- .cmds_start = 0x0,
- .ctrl_start = 0x0,
- .cdt = 0x0,
- .fifo_start = 0x0,
- .fifo_size = 0x0,
+ .name = "TS1 B",
+ .cmds_start = 0x100A0,
+ .ctrl_start = 0x10780,
+ .cdt = 0x10400,
+ .fifo_start = 0x5000,
+ .fifo_size = 0x1000,
.ptr1_reg = DMA3_PTR1,
.ptr2_reg = DMA3_PTR2,
.cnt1_reg = DMA3_CNT1,
@@ -357,7 +359,7 @@ static int cx23885_risc_decode(u32 risc)
}
void cx23885_wakeup(struct cx23885_tsport *port,
- struct cx23885_dmaqueue *q, u32 count)
+ struct cx23885_dmaqueue *q, u32 count)
{
struct cx23885_dev *dev = port->dev;
struct cx23885_buffer *buf;
@@ -378,7 +380,7 @@ void cx23885_wakeup(struct cx23885_tsport *port,
do_gettimeofday(&buf->vb.ts);
dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
count, buf->count);
- buf->vb.state = STATE_DONE;
+ buf->vb.state = VIDEOBUF_DONE;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
@@ -391,12 +393,10 @@ void cx23885_wakeup(struct cx23885_tsport *port,
printk("%s: %d buffers handled (should be 1)\n",
__FUNCTION__, bc);
}
-void cx23885_sram_channel_dump(struct cx23885_dev *dev,
- struct sram_channel *ch);
int cx23885_sram_channel_setup(struct cx23885_dev *dev,
- struct sram_channel *ch,
- unsigned int bpl, u32 risc)
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
{
unsigned int i, lines;
u32 cdt;
@@ -468,7 +468,7 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev,
}
void cx23885_sram_channel_dump(struct cx23885_dev *dev,
- struct sram_channel *ch)
+ struct sram_channel *ch)
{
static char *name[] = {
"init risc lo",
@@ -529,8 +529,8 @@ void cx23885_sram_channel_dump(struct cx23885_dev *dev,
dev->name, cx_read(ch->cnt2_reg));
}
-void cx23885_risc_disasm(struct cx23885_tsport *port,
- struct btcx_riscmem *risc)
+static void cx23885_risc_disasm(struct cx23885_tsport *port,
+ struct btcx_riscmem *risc)
{
struct cx23885_dev *dev = port->dev;
unsigned int i, j, n;
@@ -548,7 +548,7 @@ void cx23885_risc_disasm(struct cx23885_tsport *port,
}
}
-void cx23885_shutdown(struct cx23885_dev *dev)
+static void cx23885_shutdown(struct cx23885_dev *dev)
{
/* disable RISC controller */
cx_write(DEV_CNTRL2, 0);
@@ -578,7 +578,7 @@ void cx23885_shutdown(struct cx23885_dev *dev)
}
-void cx23885_reset(struct cx23885_dev *dev)
+static void cx23885_reset(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __FUNCTION__);
@@ -594,15 +594,18 @@ void cx23885_reset(struct cx23885_dev *dev)
mdelay(100);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH01 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH02 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH03 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH04 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH05 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH06 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH07 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH08 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH09 ], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ 720*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH03],
+ 188*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH04], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH05], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH06],
+ 188*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH07], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH08], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH09], 128, 0);
cx23885_gpio_setup(dev);
}
@@ -637,7 +640,7 @@ static int get_resources(struct cx23885_dev *dev)
static void cx23885_timeout(unsigned long data);
int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value);
+ u32 reg, u32 mask, u32 value);
static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *port, int portno)
{
@@ -704,6 +707,44 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *p
return 0;
}
+static void cx23885_dev_checkrevision(struct cx23885_dev *dev)
+{
+ switch (cx_read(RDR_CFG2) & 0xff) {
+ case 0x00:
+ /* cx23885 */
+ dev->hwrevision = 0xa0;
+ break;
+ case 0x01:
+ /* CX23885-12Z */
+ dev->hwrevision = 0xa1;
+ break;
+ case 0x02:
+ /* CX23885-13Z */
+ dev->hwrevision = 0xb0;
+ break;
+ case 0x03:
+ /* CX23888-22Z */
+ dev->hwrevision = 0xc0;
+ break;
+ case 0x0e:
+ /* CX23887-15Z */
+ dev->hwrevision = 0xc0;
+ case 0x0f:
+ /* CX23887-14Z */
+ dev->hwrevision = 0xb1;
+ break;
+ default:
+ printk(KERN_ERR "%s() New hardware revision found 0x%x\n",
+ __FUNCTION__, dev->hwrevision);
+ }
+ if (dev->hwrevision)
+ printk(KERN_INFO "%s() Hardware revision = 0x%02x\n",
+ __FUNCTION__, dev->hwrevision);
+ else
+ printk(KERN_ERR "%s() Hardware revision unknown 0x%x\n",
+ __FUNCTION__, dev->hwrevision);
+}
+
static int cx23885_dev_setup(struct cx23885_dev *dev)
{
int i;
@@ -723,10 +764,14 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
if(dev->pci->device == 0x8880) {
dev->bridge = CX23885_BRIDGE_887;
dev->sram_channels = cx23887_sram_channels;
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 25000000;
} else
if(dev->pci->device == 0x8852) {
dev->bridge = CX23885_BRIDGE_885;
dev->sram_channels = cx23885_sram_channels;
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 28000000;
} else
BUG();
@@ -746,6 +791,10 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_card_list(dev);
}
+ /* If the user specific a clk freq override, apply it */
+ if (cx23885_boards[dev->board].clk_freq > 0)
+ dev->clk_freq = cx23885_boards[dev->board].clk_freq;
+
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
dev->pci_irqmask = 0x001f00;
@@ -810,6 +859,17 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_pci_quirks(dev);
+ /* Assume some sensible defaults */
+ dev->tuner_type = cx23885_boards[dev->board].tuner_type;
+ dev->tuner_addr = cx23885_boards[dev->board].tuner_addr;
+ dev->radio_type = cx23885_boards[dev->board].radio_type;
+ dev->radio_addr = cx23885_boards[dev->board].radio_addr;
+
+ dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x\n",
+ __FUNCTION__, dev->tuner_type, dev->tuner_addr);
+ dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n",
+ __FUNCTION__, dev->radio_type, dev->radio_addr);
+
/* init hardware */
cx23885_reset(dev);
@@ -820,24 +880,33 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_card_setup(dev);
cx23885_ir_init(dev);
- if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
+ if (cx23885_video_register(dev) < 0) {
+ printk(KERN_ERR "%s() Failed to register analog "
+ "video adapters on VID_A\n", __FUNCTION__);
+ }
+ }
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
if (cx23885_dvb_register(&dev->ts1) < 0) {
printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n",
__FUNCTION__);
}
}
- if(cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
if (cx23885_dvb_register(&dev->ts2) < 0) {
printk(KERN_ERR "%s() Failed to register dvb adapters on VID_C\n",
__FUNCTION__);
}
}
+ cx23885_dev_checkrevision(dev);
+
return 0;
}
-void cx23885_dev_unregister(struct cx23885_dev *dev)
+static void cx23885_dev_unregister(struct cx23885_dev *dev)
{
release_mem_region(pci_resource_start(dev->pci,0),
pci_resource_len(dev->pci,0));
@@ -845,6 +914,9 @@ void cx23885_dev_unregister(struct cx23885_dev *dev)
if (!atomic_dec_and_test(&dev->refcount))
return;
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO)
+ cx23885_video_unregister(dev);
+
if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
cx23885_dvb_unregister(&dev->ts1);
@@ -952,9 +1024,11 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
return 0;
}
-int cx23885_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
- struct scatterlist *sglist, unsigned int bpl,
- unsigned int lines)
+static int cx23885_risc_databuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines)
{
u32 instructions;
u32 *rp;
@@ -982,7 +1056,7 @@ int cx23885_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
}
int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value)
+ u32 reg, u32 mask, u32 value)
{
u32 *rp;
int rc;
@@ -1011,7 +1085,58 @@ void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf)
videobuf_dma_unmap(q, dma);
videobuf_dma_free(dma);
btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
+{
+ struct cx23885_dev *dev = port->dev;
+
+ dprintk(1, "%s() Register Dump\n", __FUNCTION__);
+ dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __FUNCTION__,
+ cx_read(DEV_CNTRL2));
+ dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(PCI_INT_MSK));
+ dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(AUDIO_INT_INT_MSK));
+ dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __FUNCTION__,
+ cx_read(AUD_INT_DMA_CTL));
+ dprintk(1, "%s() AUD_EXT_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(AUDIO_EXT_INT_MSK));
+ dprintk(1, "%s() AUD_EXT_DMA_CTL 0x%08X\n", __FUNCTION__,
+ cx_read(AUD_EXT_DMA_CTL));
+ dprintk(1, "%s() PAD_CTRL 0x%08X\n", __FUNCTION__,
+ cx_read(PAD_CTRL));
+ dprintk(1, "%s() ALT_PIN_OUT_SEL 0x%08X\n", __FUNCTION__,
+ cx_read(ALT_PIN_OUT_SEL));
+ dprintk(1, "%s() GPIO2 0x%08X\n", __FUNCTION__,
+ cx_read(GPIO2));
+ dprintk(1, "%s() gpcnt(0x%08X) 0x%08X\n", __FUNCTION__,
+ port->reg_gpcnt, cx_read(port->reg_gpcnt));
+ dprintk(1, "%s() gpcnt_ctl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_gpcnt_ctl, cx_read(port->reg_gpcnt_ctl));
+ dprintk(1, "%s() dma_ctl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_dma_ctl, cx_read(port->reg_dma_ctl));
+ dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_src_sel, cx_read(port->reg_src_sel));
+ dprintk(1, "%s() lngth(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_lngth, cx_read(port->reg_lngth));
+ dprintk(1, "%s() hw_sop_ctrl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_hw_sop_ctrl, cx_read(port->reg_hw_sop_ctrl));
+ dprintk(1, "%s() gen_ctrl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_gen_ctrl, cx_read(port->reg_gen_ctrl));
+ dprintk(1, "%s() bd_pkt_status(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_bd_pkt_status, cx_read(port->reg_bd_pkt_status));
+ dprintk(1, "%s() sop_status(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_sop_status, cx_read(port->reg_sop_status));
+ dprintk(1, "%s() fifo_ovfl_stat(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_fifo_ovfl_stat, cx_read(port->reg_fifo_ovfl_stat));
+ dprintk(1, "%s() vld_misc(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_vld_misc, cx_read(port->reg_vld_misc));
+ dprintk(1, "%s() ts_clk_en(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en));
+ dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk));
}
static int cx23885_start_dma(struct cx23885_tsport *port,
@@ -1076,6 +1201,9 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */
+ if (debug > 4)
+ cx23885_tsport_reg_dump(port);
+
return 0;
}
@@ -1091,7 +1219,7 @@ static int cx23885_stop_dma(struct cx23885_tsport *port)
return 0;
}
-static int cx23885_restart_queue(struct cx23885_tsport *port,
+int cx23885_restart_queue(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q)
{
struct cx23885_dev *dev = port->dev;
@@ -1114,7 +1242,7 @@ static int cx23885_restart_queue(struct cx23885_tsport *port,
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue, &q->active);
cx23885_start_dma(port, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(5, "[%p/%d] restart_queue - first active\n",
@@ -1125,7 +1253,7 @@ static int cx23885_restart_queue(struct cx23885_tsport *port,
prev->fmt == buf->fmt) {
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue, &q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */
@@ -1162,7 +1290,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
buf->vb.width = port->ts_packet_size;
buf->vb.height = port->ts_packet_count;
buf->vb.size = size;
@@ -1174,7 +1302,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
videobuf_to_dma(&buf->vb)->sglist,
buf->vb.width, buf->vb.height);
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -1197,7 +1325,7 @@ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf)
dprintk( 1, "queue is empty - first active\n" );
list_add_tail(&buf->vb.queue, &cx88q->active);
cx23885_start_dma(port, cx88q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT);
dprintk(1, "[%p/%d] %s - first active\n",
@@ -1207,7 +1335,7 @@ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf)
prev = list_entry(cx88q->active.prev, struct cx23885_buffer,
vb.queue);
list_add_tail(&buf->vb.queue, &cx88q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */
@@ -1231,7 +1359,7 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
buf = list_entry(q->active.next, struct cx23885_buffer,
vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
dprintk(1, "[%p/%d] %s - dma=0x%08lx\n",
buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
@@ -1243,16 +1371,6 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
spin_unlock_irqrestore(&port->slock, flags);
}
-void cx23885_cancel_buffers(struct cx23885_tsport *port)
-{
- struct cx23885_dev *dev = port->dev;
- struct cx23885_dmaqueue *q = &port->mpegq;
-
- dprintk(1, "%s()\n", __FUNCTION__);
- del_timer_sync(&q->timeout);
- cx23885_stop_dma(port);
- do_cancel_buffers(port, "cancel", 0);
-}
static void cx23885_timeout(unsigned long data)
{
@@ -1325,12 +1443,15 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
struct cx23885_tsport *ts1 = &dev->ts1;
struct cx23885_tsport *ts2 = &dev->ts2;
u32 pci_status, pci_mask;
+ u32 vida_status, vida_mask;
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
- int ts1_count = 0, ts2_count = 0, handled = 0;
+ int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx_read(PCI_INT_MSK);
+ vida_status = cx_read(VID_A_INT_STAT);
+ vida_mask = cx_read(VID_A_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
ts1_mask = cx_read(VID_B_INT_MSK);
ts2_status = cx_read(VID_C_INT_STAT);
@@ -1339,11 +1460,17 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
if ( (pci_status == 0) && (ts2_status == 0) && (ts1_status == 0) )
goto out;
+ vida_count = cx_read(VID_A_GPCNT);
ts1_count = cx_read(ts1->reg_gpcnt);
ts2_count = cx_read(ts2->reg_gpcnt);
- dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", pci_status, pci_mask );
- dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n", ts1_status, ts1_mask, ts1_count );
- dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", ts2_status, ts2_mask, ts2_count );
+ dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n",
+ pci_status, pci_mask);
+ dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n",
+ vida_status, vida_mask, vida_count);
+ dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n",
+ ts1_status, ts1_mask, ts1_count);
+ dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n",
+ ts2_status, ts2_mask, ts2_count);
if ( (pci_status & PCI_MSK_RISC_RD) ||
(pci_status & PCI_MSK_RISC_WR) ||
@@ -1380,11 +1507,18 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
}
- if (ts1_status)
- handled += cx23885_irq_ts(ts1, ts1_status);
+ if (ts1_status) {
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts1, ts1_status);
+ }
+
+ if (ts2_status) {
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts2, ts2_status);
+ }
- if (ts2_status)
- handled += cx23885_irq_ts(ts2, ts2_status);
+ if (vida_status)
+ handled += cx23885_video_irq(dev, vida_status);
if (handled)
cx_write(PCI_INT_STAT, pci_status);
diff --git a/trunk/drivers/media/video/cx23885/cx23885-dvb.c b/trunk/drivers/media/video/cx23885/cx23885-dvb.c
index eda8c05d0931..ed465c007cea 100644
--- a/trunk/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/trunk/drivers/media/video/cx23885/cx23885-dvb.c
@@ -32,13 +32,26 @@
#include "s5h1409.h"
#include "mt2131.h"
+#include "tda8290.h"
+#include "tda18271.h"
#include "lgdt330x.h"
+#include "xc5000.h"
#include "dvb-pll.h"
+#include "tuner-xc2028.h"
+#include "tuner-xc2028-types.h"
-static unsigned int debug = 0;
+static unsigned int debug;
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg)
+#define dprintk(level, fmt, arg...)\
+ do { if (debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int alt_tuner;
+module_param(alt_tuner, int, 0644);
+MODULE_PARM_DESC(alt_tuner, "Enable alternate tuner configuration");
/* ------------------------------------------------------------------ */
@@ -85,18 +98,39 @@ static struct s5h1409_config hauppauge_generic_config = {
.demod_address = 0x32 >> 1,
.output_mode = S5H1409_SERIAL_OUTPUT,
.gpio = S5H1409_GPIO_ON,
- .if_freq = 44000,
+ .qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config hauppauge_ezqam_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 4000,
+ .inversion = S5H1409_INVERSION_ON,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
};
static struct s5h1409_config hauppauge_hvr1800lp_config = {
.demod_address = 0x32 >> 1,
.output_mode = S5H1409_SERIAL_OUTPUT,
.gpio = S5H1409_GPIO_OFF,
- .if_freq = 44000,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config hauppauge_hvr1500_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
.inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
};
static struct mt2131_config hauppauge_generic_tunerconfig = {
@@ -109,6 +143,66 @@ static struct lgdt330x_config fusionhdtv_5_express = {
.serial_mpeg = 0x40,
};
+static struct s5h1409_config hauppauge_hvr1500q_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
+ .i2c_address = 0x61,
+ .if_khz = 5380,
+ .tuner_callback = cx23885_tuner_callback
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .std_bits = 0x1b },
+ .qam_6 = { .if_freq = 4000, .std_bits = 0x18 },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg)
+{
+ struct cx23885_tsport *port = ptr;
+ struct cx23885_dev *dev = port->dev;
+
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ /* Send the tuner in then out of reset */
+ /* GPIO-2 xc3028 tuner */
+ dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __FUNCTION__, arg);
+
+ cx_set(GP0_IO, 0x00040000);
+ cx_clear(GP0_IO, 0x00000004);
+ msleep(5);
+
+ cx_set(GP0_IO, 0x00040004);
+ msleep(5);
+ break;
+ case XC2028_RESET_CLK:
+ dprintk(1, "%s: XC2028_RESET_CLK %d\n", __FUNCTION__, arg);
+ break;
+ default:
+ dprintk(1, "%s: unknown command %d, arg %d\n", __FUNCTION__,
+ command, arg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int dvb_register(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
@@ -120,7 +214,6 @@ static int dvb_register(struct cx23885_tsport *port)
/* init frontend */
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
- case CX23885_BOARD_HAUPPAUGE_HVR1800:
i2c_bus = &dev->i2c_bus[0];
port->dvb.frontend = dvb_attach(s5h1409_attach,
&hauppauge_generic_config,
@@ -131,6 +224,36 @@ static int dvb_register(struct cx23885_tsport *port)
&hauppauge_generic_tunerconfig, 0);
}
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ i2c_bus = &dev->i2c_bus[0];
+ switch (alt_tuner) {
+ case 1:
+ port->dvb.frontend =
+ dvb_attach(s5h1409_attach,
+ &hauppauge_ezqam_config,
+ &i2c_bus->i2c_adap);
+ if (port->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, port->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, port->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+ }
+ break;
+ case 0:
+ default:
+ port->dvb.frontend =
+ dvb_attach(s5h1409_attach,
+ &hauppauge_generic_config,
+ &i2c_bus->i2c_adap);
+ if (port->dvb.frontend != NULL)
+ dvb_attach(mt2131_attach, port->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
+ break;
+ }
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
i2c_bus = &dev->i2c_bus[0];
port->dvb.frontend = dvb_attach(s5h1409_attach,
@@ -152,6 +275,43 @@ static int dvb_register(struct cx23885_tsport *port)
&i2c_bus->i2c_adap, DVB_PLL_LG_TDVS_H06XF);
}
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ i2c_bus = &dev->i2c_bus[1];
+ port->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1500q_config,
+ &dev->i2c_bus[0].i2c_adap);
+ if (port->dvb.frontend != NULL) {
+ hauppauge_hvr1500q_tunerconfig.priv = i2c_bus;
+ dvb_attach(xc5000_attach, port->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_hvr1500q_tunerconfig);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ i2c_bus = &dev->i2c_bus[1];
+ port->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1500_config,
+ &dev->i2c_bus[0].i2c_adap);
+ if (port->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &i2c_bus->i2c_adap,
+ .i2c_addr = 0x61,
+ .video_dev = port,
+ .callback = cx23885_hvr1500_xc3028_callback,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = "xc3028-v27.fw",
+ .max_len = 64,
+ .scode_table = OREN538,
+ };
+
+ fe = dvb_attach(xc2028_attach,
+ port->dvb.frontend, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
default:
printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->name);
@@ -165,6 +325,9 @@ static int dvb_register(struct cx23885_tsport *port)
/* Put the analog decoder in standby to keep it quiet */
cx23885_call_i2c_clients(i2c_bus, TUNER_SET_STANDBY, NULL);
+ if (port->dvb.frontend->ops.analog_ops.standby)
+ port->dvb.frontend->ops.analog_ops.standby(port->dvb.frontend);
+
/* register everything */
return videobuf_dvb_register(&port->dvb, THIS_MODULE, port,
&dev->pci->dev);
diff --git a/trunk/drivers/media/video/cx23885/cx23885-i2c.c b/trunk/drivers/media/video/cx23885/cx23885-i2c.c
index 71da528932df..92fe0bd37c84 100644
--- a/trunk/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/trunk/drivers/media/video/cx23885/cx23885-i2c.c
@@ -29,7 +29,7 @@
#include
-static unsigned int i2c_debug = 0;
+static unsigned int i2c_debug;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
@@ -37,8 +37,10 @@ static unsigned int i2c_scan = 0;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
-#define dprintk(level,fmt, arg...) if (i2c_debug >= level) \
- printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)
+#define dprintk(level, fmt, arg...)\
+ do { if (i2c_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
#define I2C_WAIT_DELAY 32
#define I2C_WAIT_RETRY 64
@@ -77,14 +79,19 @@ static int i2c_wait_done(struct i2c_adapter *i2c_adap)
}
static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
- const struct i2c_msg *msg, int last)
+ const struct i2c_msg *msg, int joined_rlen)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
u32 wdata, addr, ctrl;
int retval, cnt;
- dprintk(1, "%s()\n", __FUNCTION__);
+ if (joined_rlen)
+ dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __FUNCTION__,
+ msg->len, joined_rlen);
+ else
+ dprintk(1, "%s(msg->len=%d)\n", __FUNCTION__, msg->len);
+
/* Deal with i2c probe functions with zero payload */
if (msg->len == 0) {
cx_write(bus->reg_addr, msg->addr << 25);
@@ -106,6 +113,8 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
if (msg->len > 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
+ else if (joined_rlen)
+ ctrl |= I2C_NOSTOP;
cx_write(bus->reg_addr, addr);
cx_write(bus->reg_wdata, wdata);
@@ -127,8 +136,10 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
wdata = msg->buf[cnt];
ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
- if (cnt < msg->len-1 || !last)
+ if (cnt < msg->len - 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
+ else if (joined_rlen)
+ ctrl |= I2C_NOSTOP;
cx_write(bus->reg_addr, addr);
cx_write(bus->reg_wdata, wdata);
@@ -150,19 +161,22 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
eio:
retval = -EIO;
err:
- printk(" ERR: %d\n", retval);
+ if (i2c_debug)
+ printk(" ERR: %d\n", retval);
return retval;
}
static int i2c_readbytes(struct i2c_adapter *i2c_adap,
- const struct i2c_msg *msg, int last)
+ const struct i2c_msg *msg, int joined)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
u32 ctrl, cnt;
int retval;
- dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (i2c_debug && !joined)
+ dprintk(1, "%s(msg->len=%d)\n", __FUNCTION__, msg->len);
/* Deal with i2c probe functions with zero payload */
if (msg->len == 0) {
@@ -178,11 +192,18 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
return 0;
}
+ if (i2c_debug) {
+ if (joined)
+ printk(" R");
+ else
+ printk(" addr << 1) + 1);
+ }
+
for(cnt = 0; cnt < msg->len; cnt++) {
ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1;
- if (cnt < msg->len-1 || !last)
+ if (cnt < msg->len - 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
cx_write(bus->reg_addr, msg->addr << 25);
@@ -195,9 +216,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
goto eio;
msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
if (i2c_debug) {
- if (!(ctrl & I2C_NOSTOP))
- printk(" addr << 1) +1);
- printk(" =%02x", msg->buf[cnt]);
+ printk(" %02x", msg->buf[cnt]);
if (!(ctrl & I2C_NOSTOP))
printk(" >\n");
}
@@ -207,7 +226,8 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
eio:
retval = -EIO;
err:
- printk(" ERR: %d\n", retval);
+ if (i2c_debug)
+ printk(" ERR: %d\n", retval);
return retval;
}
@@ -225,15 +245,22 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap,
__FUNCTION__, num, msgs[i].addr, msgs[i].len);
if (msgs[i].flags & I2C_M_RD) {
/* read */
- retval = i2c_readbytes(i2c_adap, &msgs[i], i+1 == num);
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+ /* write then read from same address */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i],
+ msgs[i + 1].len);
if (retval < 0)
goto err;
+ i++;
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
} else {
/* write */
- retval = i2c_sendbytes(i2c_adap, &msgs[i], i+1 == num);
- if (retval < 0)
- goto err;
+ retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
}
+ if (retval < 0)
+ goto err;
}
return num;
@@ -243,7 +270,9 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap,
static int attach_inform(struct i2c_client *client)
{
- struct cx23885_dev *dev = i2c_get_adapdata(client->adapter);
+ struct cx23885_i2c *bus = i2c_get_adapdata(client->adapter);
+ struct cx23885_dev *dev = bus->dev;
+ struct tuner_setup tun_setup;
dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
client->driver->driver.name, client->addr, client->name);
@@ -251,6 +280,31 @@ static int attach_inform(struct i2c_client *client)
if (!client->driver->command)
return 0;
+ if (dev->tuner_type != UNSET) {
+
+ dprintk(1, "%s (tuner) i2c attach [addr=0x%x,client=%s]\n",
+ client->driver->driver.name, client->addr,
+ client->name);
+
+ if ((dev->tuner_addr == ADDR_UNSET) ||
+ (dev->tuner_addr == client->addr)) {
+
+ dprintk(1, "%s (tuner || addr UNSET)\n",
+ client->driver->driver.name);
+
+ dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
+ client->driver->driver.name,
+ client->addr, client->name);
+
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+
+ client->driver->command(client, TUNER_SET_TYPE_ADDR,
+ &tun_setup);
+ }
+ }
+
return 0;
}
@@ -289,6 +343,7 @@ static struct i2c_adapter cx23885_i2c_adap_template = {
.owner = THIS_MODULE,
.id = I2C_HW_B_CX23885,
.algo = &cx23885_i2c_algo_template,
+ .class = I2C_CLASS_TV_ANALOG,
.client_register = attach_inform,
.client_unregister = detach_inform,
};
@@ -305,7 +360,7 @@ static char *i2c_devs[128] = {
[ 0x84 >> 1 ] = "tda8295",
[ 0xa0 >> 1 ] = "eeprom",
[ 0xc0 >> 1 ] = "tuner/mt2131/tda8275",
- [ 0xc2 >> 1 ] = "tuner/mt2131/tda8275",
+ [ 0xc2 >> 1 ] = "tuner/mt2131/tda8275/xc5000",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
@@ -344,6 +399,7 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
+ i2c_set_adapdata(&bus->i2c_adap, bus);
i2c_add_adapter(&bus->i2c_adap);
bus->i2c_client.adapter = &bus->i2c_adap;
@@ -366,8 +422,6 @@ int cx23885_i2c_unregister(struct cx23885_i2c *bus)
/* ----------------------------------------------------------------------- */
-EXPORT_SYMBOL(cx23885_call_i2c_clients);
-
/*
* Local variables:
* c-basic-offset: 8
diff --git a/trunk/drivers/media/video/cx23885/cx23885-reg.h b/trunk/drivers/media/video/cx23885/cx23885-reg.h
index 162169f9091b..bdd11bc513ad 100644
--- a/trunk/drivers/media/video/cx23885/cx23885-reg.h
+++ b/trunk/drivers/media/video/cx23885/cx23885-reg.h
@@ -233,6 +233,17 @@ Channel manager Data Structure entry = 20 DWORD
#define VID_A_INT_SSTAT 0x0004002C
#define VID_B_INT_MSK 0x00040030
+#define VID_B_MSK_BAD_PKT (1 << 20)
+#define VID_B_MSK_VBI_OPC_ERR (1 << 17)
+#define VID_B_MSK_OPC_ERR (1 << 16)
+#define VID_B_MSK_VBI_SYNC (1 << 13)
+#define VID_B_MSK_SYNC (1 << 12)
+#define VID_B_MSK_VBI_OF (1 << 9)
+#define VID_B_MSK_OF (1 << 8)
+#define VID_B_MSK_VBI_RISCI2 (1 << 5)
+#define VID_B_MSK_RISCI2 (1 << 4)
+#define VID_B_MSK_VBI_RISCI1 (1 << 1)
+#define VID_B_MSK_RISCI1 1
#define VID_B_INT_STAT 0x00040034
#define VID_B_INT_MSTAT 0x00040038
#define VID_B_INT_SSTAT 0x0004003C
@@ -276,6 +287,7 @@ Channel manager Data Structure entry = 20 DWORD
#define RDR_CFG0 0x00050000
#define RDR_CFG1 0x00050004
+#define RDR_CFG2 0x00050008
#define RDR_TLCTL0 0x00050318
/* APB DMAC Current Buffer Pointer */
@@ -335,6 +347,7 @@ Channel manager Data Structure entry = 20 DWORD
/* GPIO (417 Microsoftcontroller) Output Enable, Low Active */
#define MC417_OEN 0x00110024
#define MC417_CTL 0x00110028
+#define ALT_PIN_OUT_SEL 0x0011002C
#define CLK_DELAY 0x00110048
#define PAD_CTRL 0x0011004C
diff --git a/trunk/drivers/media/video/cx23885/cx23885-vbi.c b/trunk/drivers/media/video/cx23885/cx23885-vbi.c
new file mode 100644
index 000000000000..e36e3fcae2fb
--- /dev/null
+++ b/trunk/drivers/media/video/cx23885/cx23885-vbi.c
@@ -0,0 +1,258 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "cx23885.h"
+
+static unsigned int vbibufs = 4;
+module_param(vbibufs, int, 0644);
+MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (vbi_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------ */
+
+int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (dev->tvnorm & V4L2_STD_525_60) {
+ /* ntsc */
+ f->fmt.vbi.sampling_rate = 28636363;
+ f->fmt.vbi.start[0] = 10;
+ f->fmt.vbi.start[1] = 273;
+
+ } else if (dev->tvnorm & V4L2_STD_625_50) {
+ /* pal */
+ f->fmt.vbi.sampling_rate = 35468950;
+ f->fmt.vbi.start[0] = 7 - 1;
+ f->fmt.vbi.start[1] = 319 - 1;
+ }
+ return 0;
+}
+
+static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02],
+ buf->vb.width, buf->risc.dma);
+
+ /* reset counter */
+ q->count = 1;
+
+ /* enable irqs */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx_set(VID_A_INT_MSK, 0x000022);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x00000022);
+
+ return 0;
+}
+
+int cx23885_stop_vbi_dma(struct cx23885_dev *dev)
+{
+ /* stop dma */
+ cx_clear(VID_A_DMA_CTL, 0x00000022);
+
+ /* disable irqs */
+ cx_clear(PCI_INT_MSK, 0x000001);
+ cx_clear(VID_A_INT_MSK, 0x00000022);
+ return 0;
+}
+
+int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf;
+ struct list_head *item;
+
+ if (list_empty(&q->active))
+ return 0;
+
+ buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_vbi_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer, vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+void cx23885_vbi_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vbiq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH02]);
+
+ cx_clear(VID_A_DMA_CTL, 0x22);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
+ buf, buf->vb.i, (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_vbi_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+/* ------------------------------------------------------------------ */
+#define VBI_LINE_LENGTH 2048
+#define VBI_LINE_COUNT 17
+
+static int
+vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+ if (0 == *count)
+ *count = vbibufs;
+ if (*count < 2)
+ *count = 2;
+ if (*count > 32)
+ *count = 32;
+ return 0;
+}
+
+static int
+vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ unsigned int size;
+ int rc;
+
+ size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = VBI_LINE_LENGTH;
+ buf->vb.height = VBI_LINE_COUNT;
+ buf->vb.size = size;
+ buf->vb.field = V4L2_FIELD_SEQ_TB;
+
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->vb.width * buf->vb.height,
+ buf->vb.width, 0,
+ buf->vb.height);
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vbiq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_vbi_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] vbi_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63-32 */
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+ }
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+struct videobuf_queue_ops cx23885_vbi_qops = {
+ .buf_setup = vbi_setup,
+ .buf_prepare = vbi_prepare,
+ .buf_queue = vbi_queue,
+ .buf_release = vbi_release,
+};
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/trunk/drivers/media/video/cx23885/cx23885-video.c b/trunk/drivers/media/video/cx23885/cx23885-video.c
new file mode 100644
index 000000000000..d3c4d2c5cbe0
--- /dev/null
+++ b/trunk/drivers/media/video/cx23885/cx23885-video.c
@@ -0,0 +1,1557 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "cx23885.h"
+#include
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+/* Include V4L1 specific functions. Should be removed soon */
+#include
+#endif
+
+MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
+MODULE_AUTHOR("Steven Toth ");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+static unsigned int irq_debug;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (video_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------- */
+/* static data */
+
+#define FORMAT_FLAGS_PACKED 0x01
+
+static struct cx23885_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+
+static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+
+ printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __FUNCTION__, fourcc);
+ return NULL;
+}
+
+/* ------------------------------------------------------------------- */
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct cx23885_ctrl cx23885_ctls[] = {
+ /* --- video --- */
+ {
+ .v = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = LUMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .v = {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = LUMA_CTRL,
+ .mask = 0xff00,
+ .shift = 8,
+ }, {
+ .v = {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = CHROMA_CTRL,
+ .mask = 0xff0000,
+ .shift = 16,
+ }, {
+ /* strictly, this only describes only U saturation.
+ * V saturation is handled specially through code.
+ */
+ .v = {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = CHROMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ /* --- audio --- */
+ .v = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = PATH1_CTL1,
+ .mask = (0x1f << 24),
+ .shift = 24,
+ }, {
+ .v = {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = PATH1_VOL_CTL,
+ .mask = 0xff,
+ .shift = 0,
+ }
+};
+static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
+
+const u32 cx23885_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_AUDIO_MUTE,
+ 0
+};
+EXPORT_SYMBOL(cx23885_user_ctrls);
+
+static const u32 *ctrl_classes[] = {
+ cx23885_user_ctrls,
+ NULL
+};
+
+void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count)
+{
+ struct cx23885_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+
+ do_gettimeofday(&buf->vb.ts);
+ dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
+ count, buf->count);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active)) {
+ del_timer(&q->timeout);
+ } else {
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ }
+ if (bc != 1)
+ printk(KERN_ERR "%s: %d buffers handled (should be 1)\n",
+ __FUNCTION__, bc);
+}
+
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
+{
+ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+ __FUNCTION__,
+ (unsigned int)norm,
+ v4l2_norm_to_name(norm));
+
+ dev->tvnorm = norm;
+
+ /* Tell the analog tuner/demods */
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_STD, &norm);
+
+ /* Tell the internal A/V decoder */
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_STD, &norm);
+
+ return 0;
+}
+
+struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+ vfd->dev = &pci->dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ dev->name, type, cx23885_boards[dev->board].name);
+ return vfd;
+}
+
+int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE ||
+ qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX23885_CTLS; i++)
+ if (cx23885_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX23885_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx23885_ctls[i].v;
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_ctrl_query);
+
+/* ------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bit)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+static int res_check(struct cx23885_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static int res_locked(struct cx23885_dev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
+{
+ struct v4l2_routing route;
+ memset(&route, 0, sizeof(route));
+
+ dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ __FUNCTION__,
+ input, INPUT(input)->vmux,
+ INPUT(input)->gpio0, INPUT(input)->gpio1,
+ INPUT(input)->gpio2, INPUT(input)->gpio3);
+ dev->input = input;
+
+ route.input = INPUT(input)->vmux;
+
+ /* Tell the internal A/V decoder */
+ cx23885_call_i2c_clients(&dev->i2c_bus[2],
+ VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_video_mux);
+
+/* ------------------------------------------------------------------ */
+int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width,
+ unsigned int height, enum v4l2_field field)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ return 0;
+}
+
+static int cx23885_start_video_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ buf->bpl, buf->risc.dma);
+ cx23885_set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field);
+
+ /* reset counter */
+ cx_write(VID_A_GPCNT_CTL, 3);
+ q->count = 1;
+
+ /* enable irq */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx_set(VID_A_INT_MSK, 0x000011);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x11); /* FIFO and RISC enable */
+
+ return 0;
+}
+
+
+static int cx23885_restart_video_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf, *prev;
+ struct list_head *item;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_video_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer,
+ vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx23885_buffer,
+ vb.queue);
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] restart_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
+ dprintk(2, "[%p/%d] restart_queue - move to active\n",
+ buf, buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct cx23885_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = 32;
+ while (*size * *count > vid_limit * 1024 * 1024)
+ (*count)--;
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ int rc, init_buffer = 0;
+ u32 line0_offset, line1_offset;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ BUG_ON(NULL == fh->fmt);
+ if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) ||
+ fh->height < 32 || fh->height > norm_maxh(dev->tvnorm))
+ return -EINVAL;
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ }
+
+ if (init_buffer) {
+ buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ if (dev->tvnorm & V4L2_STD_NTSC) {
+ /* cx25840 transmits NTSC bottom field first */
+ dprintk(1, "%s() Creating NTSC risc\n",
+ __FUNCTION__);
+ line0_offset = buf->bpl;
+ line1_offset = 0;
+ } else {
+ /* All other formats are top field first */
+ dprintk(1, "%s() Creating PAL/SECAM risc\n",
+ __FUNCTION__);
+ line0_offset = 0;
+ line1_offset = buf->bpl;
+ }
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, line0_offset,
+ line1_offset,
+ buf->bpl, buf->bpl,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i,
+ fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+ (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+
+ } else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+static struct videobuf_queue_ops cx23885_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static struct videobuf_queue *get_queue(struct cx23885_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &fh->vidq;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return &fh->vbiq;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+static int get_resource(struct cx23885_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static int video_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct cx23885_dev *h, *dev = NULL;
+ struct cx23885_fh *fh;
+ struct list_head *list;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ list_for_each(list, &cx23885_devlist) {
+ h = list_entry(list, struct cx23885_dev, devlist);
+ if (h->video_dev->minor == minor) {
+ dev = h;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ }
+ if (h->vbi_dev &&
+ h->vbi_dev->minor == minor) {
+ dev = h;
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ }
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ radio = 1;
+ dev = h;
+ }
+ }
+ if (NULL == dev)
+ return -ENODEV;
+
+ dprintk(1, "open minor=%d radio=%d type=%s\n",
+ minor, radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->width = 320;
+ fh->height = 240;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
+ videobuf_queue_pci_init(&fh->vidq, &cx23885_video_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx23885_buffer),
+ fh);
+
+ dprintk(1, "post videobuf_queue_init()\n");
+
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int video_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_buffer *buf;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbiq, wait);
+ }
+
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ return POLLERR;
+ buf = list_entry(fh->vidq.stream.next,
+ struct cx23885_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx23885_buffer *)fh->vidq.read_buf;
+ if (NULL == buf)
+ return POLLERR;
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN|POLLRDNORM;
+ return 0;
+}
+
+static int video_release(struct inode *inode, struct file *file)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ /* FIXME */
+ res_free(dev, fh, RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_queue_cancel(&fh->vidq);
+ res_free(dev, fh, RESOURCE_VIDEO);
+ }
+ if (fh->vidq.read_buf) {
+ buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ if (fh->vbiq.streaming)
+ videobuf_streamoff(&fh->vbiq);
+ if (fh->vbiq.reading)
+ videobuf_read_stop(&fh->vbiq);
+ res_free(dev, fh, RESOURCE_VBI);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+ file->private_data = NULL;
+ kfree(fh);
+
+ /* We are not putting the tuner to sleep here on exit, because
+ * we want to use the mpeg encoder in another session to capture
+ * tuner video. Closing this will result in no video to the encoder.
+ */
+
+ return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __FUNCTION__);
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl);
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_get_control);
+
+int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)"
+ " (disabled - no action)\n", __FUNCTION__);
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_set_control);
+
+static void init_controls(struct cx23885_dev *dev)
+{
+ struct v4l2_control ctrl;
+ int i;
+
+ for (i = 0; i < CX23885_CTLS; i++) {
+ ctrl.id = cx23885_ctls[i].v.id;
+ ctrl.value = cx23885_ctls[i].v.default_value;
+
+ cx23885_set_control(dev, &ctrl);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS */
+
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ struct cx23885_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = norm_maxw(dev->tvnorm);
+ maxh = norm_maxh(dev->tvnorm);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ int err;
+
+ dprintk(2, "%s()\n", __FUNCTION__);
+ err = vidioc_try_fmt_cap(file, priv, f);
+
+ if (0 != err)
+ return err;
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->vidq.field = f->fmt.pix.field;
+ dprintk(2, "%s() width=%d height=%d field=%d\n", __FUNCTION__,
+ fh->width, fh->height, fh->vidq.field);
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_FMT, f);
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ strcpy(cap->driver, "cx23885");
+ strlcpy(cap->card, cx23885_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+ cap->version = CX23885_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_VBI_CAPTURE;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv,
+ struct video_mbuf *mbuf)
+{
+ struct cx23885_fh *fh = priv;
+ struct videobuf_queue *q;
+ struct v4l2_requestbuffers req;
+ unsigned int i;
+ int err;
+
+ q = get_queue(fh);
+ memset(&req, 0, sizeof(req));
+ req.type = q->type;
+ req.count = 8;
+ req.memory = V4L2_MEMORY_MMAP;
+ err = videobuf_reqbufs(q, &req);
+ if (err < 0)
+ return err;
+
+ mbuf->frames = req.count;
+ mbuf->size = 0;
+ for (i = 0; i < mbuf->frames; i++) {
+ mbuf->offsets[i] = q->bufs[i]->boff;
+ mbuf->size += q->bufs[i]->bsize;
+ }
+ return 0;
+}
+#endif
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_reqbufs(get_queue(fh), p));
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_querybuf(get_queue(fh), p));
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_qbuf(get_queue(fh), p));
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_dqbuf(get_queue(fh), p,
+ file->f_flags & O_NONBLOCK));
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return -EINVAL;
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+
+ if (unlikely(!res_get(dev, fh, get_resource(fh))))
+ return -EBUSY;
+ return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ int err, res;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ res = get_resource(fh);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, *tvnorms);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
+{
+ static const char *iname[] = {
+ [CX23885_VMUX_COMPOSITE1] = "Composite1",
+ [CX23885_VMUX_COMPOSITE2] = "Composite2",
+ [CX23885_VMUX_COMPOSITE3] = "Composite3",
+ [CX23885_VMUX_COMPOSITE4] = "Composite4",
+ [CX23885_VMUX_SVIDEO] = "S-Video",
+ [CX23885_VMUX_TELEVISION] = "Television",
+ [CX23885_VMUX_CABLE] = "Cable TV",
+ [CX23885_VMUX_DVB] = "DVB",
+ [CX23885_VMUX_DEBUG] = "for debug only",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ n = i->index;
+ if (n >= 4)
+ return -EINVAL;
+
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, iname[INPUT(n)->type]);
+ if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
+ (CX23885_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->std = CX23885_NORMS;
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_enum_input);
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+ return cx23885_enum_input(dev, i);
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ *i = dev->input;
+ dprintk(1, "%s() returns %d\n", __FUNCTION__, *i);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ dprintk(1, "%s(%d)\n", __FUNCTION__, i);
+
+ if (i >= 4) {
+ dprintk(1, "%s() -EINVAL\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->lock);
+ cx23885_video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (unlikely(qctrl->id == 0))
+ return -EINVAL;
+ return cx23885_ctrl_query(qctrl);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_get_control(dev, ctl);
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_set_control(dev, ctl);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ t->signal = 0xffff ; /* LOCKED */
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+
+ /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f);
+
+ return 0;
+}
+
+int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
+{
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(10);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_set_freq);
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ return
+ cx23885_set_freq(dev, f);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *fh,
+ struct v4l2_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_G_REGISTER, reg);
+
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *fh,
+ struct v4l2_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_S_REGISTER, reg);
+
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+static void cx23885_vid_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
+
+ cx_clear(VID_A_DMA_CTL, 0x11);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk(KERN_ERR "%s/0: [%p/%d] timeout - dma=0x%08lx\n",
+ dev->name, buf, buf->vb.i,
+ (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_video_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
+{
+ u32 mask, count;
+ int handled = 0;
+
+ mask = cx_read(VID_A_INT_MSK);
+ if (0 == (status & mask))
+ return handled;
+ cx_write(VID_A_INT_STAT, status);
+
+ dprintk(2, "%s() status = 0x%08x\n", __FUNCTION__, status);
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ printk(KERN_WARNING "%s/0: video risc op code error\n",
+ dev->name);
+ cx_clear(VID_A_DMA_CTL, 0x11);
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
+ }
+
+ /* risc1 y */
+ if (status & 0x01) {
+ spin_lock(&dev->slock);
+ count = cx_read(VID_A_GPCNT);
+ cx23885_video_wakeup(dev, &dev->vidq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+ /* risc2 y */
+ if (status & 0x10) {
+ dprintk(2, "stopper video\n");
+ spin_lock(&dev->slock);
+ cx23885_restart_video_queue(dev, &dev->vidq);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ return handled;
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static const struct file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device cx23885_vbi_template;
+static struct video_device cx23885_video_template = {
+ .name = "cx23885-video",
+ .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES,
+ .fops = &video_fops,
+ .minor = -1,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
+ .vidioc_g_fmt_cap = vidioc_g_fmt_cap,
+ .vidioc_try_fmt_cap = vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = vidioc_s_fmt_cap,
+ .vidioc_g_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_try_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_s_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ .vidiocgmbuf = vidiocgmbuf,
+#endif
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .tvnorms = CX23885_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+
+void cx23885_video_unregister(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ cx_clear(PCI_INT_MSK, 1);
+
+ if (dev->video_dev) {
+ if (-1 != dev->video_dev->minor)
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+
+ btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
+ }
+}
+
+int cx23885_video_register(struct cx23885_dev *dev)
+{
+ int err;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+ spin_lock_init(&dev->slock);
+
+ /* Initialize VBI template */
+ memcpy(&cx23885_vbi_template, &cx23885_video_template,
+ sizeof(cx23885_vbi_template));
+ strcpy(cx23885_vbi_template.name, "cx23885-vbi");
+ cx23885_vbi_template.type = VID_TYPE_TELETEXT|VID_TYPE_TUNER;
+
+ dev->tvnorm = cx23885_video_template.current_norm;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ dev->vidq.timeout.function = cx23885_vid_timeout;
+ dev->vidq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vidq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
+ VID_A_DMA_CTL, 0x11, 0x00);
+
+ /* Don't enable VBI yet */
+ cx_set(PCI_INT_MSK, 1);
+
+
+ /* register v4l devices */
+ dev->video_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_video_template, "video");
+ err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
+ video_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device video%d [v4l2]\n",
+ dev->name, dev->video_dev->minor & 0x1f);
+ /* initial device configuration */
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, dev->tvnorm);
+ init_controls(dev);
+ cx23885_video_mux(dev, 0);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+fail_unreg:
+ cx23885_video_unregister(dev);
+ return err;
+}
+
diff --git a/trunk/drivers/media/video/cx23885/cx23885.h b/trunk/drivers/media/video/cx23885/cx23885.h
index dec4dc2fcbb4..7cb2179f2622 100644
--- a/trunk/drivers/media/video/cx23885/cx23885.h
+++ b/trunk/drivers/media/video/cx23885/cx23885.h
@@ -44,6 +44,10 @@
/* Max number of inputs by card */
#define MAX_CX23885_INPUT 8
+#define INPUT(nr) (&cx23885_boards[dev->board].input[nr])
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
@@ -53,6 +57,62 @@
#define CX23885_BOARD_HAUPPAUGE_HVR1800 2
#define CX23885_BOARD_HAUPPAUGE_HVR1250 3
#define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4
+#define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5
+#define CX23885_BOARD_HAUPPAUGE_HVR1500 6
+
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
+#define CX23885_NORMS (\
+ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
+ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
+ V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK)
+
+struct cx23885_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+
+struct cx23885_ctrl {
+ struct v4l2_queryctrl v;
+ u32 off;
+ u32 reg;
+ u32 mask;
+ u32 shift;
+};
+
+struct cx23885_tvnorm {
+ char *name;
+ v4l2_std_id id;
+ u32 cxiformat;
+ u32 cxoformat;
+};
+
+struct cx23885_fh {
+ struct cx23885_dev *dev;
+ enum v4l2_buf_type type;
+ int radio;
+ u32 resources;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip *clips;
+ unsigned int nclips;
+
+ /* video capture */
+ struct cx23885_fmt *fmt;
+ unsigned int width, height;
+
+ /* vbi capture */
+ struct videobuf_queue vidq;
+ struct videobuf_queue vbiq;
+
+ /* MPEG Encoder specifics ONLY */
+ struct videobuf_queue mpegq;
+ atomic_t v4l_reading;
+};
enum cx23885_itype {
CX23885_VMUX_COMPOSITE1 = 1,
@@ -92,12 +152,28 @@ struct cx23885_input {
typedef enum {
CX23885_MPEG_UNDEFINED = 0,
- CX23885_MPEG_DVB
+ CX23885_MPEG_DVB,
+ CX23885_ANALOG_VIDEO,
} port_t;
struct cx23885_board {
char *name;
- port_t portb, portc;
+ port_t porta, portb, portc;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+
+ /* Vendors can and do run the PCIe bridge at different
+ * clock rates, driven physically by crystals on the PCBs.
+ * The core has to accomodate this. This allows the user
+ * to add new boards with new frequencys. The value is
+ * expressed in Hz.
+ *
+ * The core framework will default this value based on
+ * current designs, but it can vary.
+ */
+ u32 clk_freq;
struct cx23885_input input[MAX_CX23885_INPUT];
};
@@ -189,6 +265,11 @@ struct cx23885_dev {
u32 __iomem *lmmio;
u8 __iomem *bmmio;
int pci_irqmask;
+ int hwrevision;
+
+ /* This valud is board specific and is used to configure the
+ * AV core so we see nice clean and stable video and audio. */
+ u32 clk_freq;
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
struct cx23885_i2c i2c_bus[3];
@@ -210,8 +291,31 @@ struct cx23885_dev {
CX23885_BRIDGE_885 = 885,
CX23885_BRIDGE_887 = 887,
} bridge;
+
+ /* Analog video */
+ u32 resources;
+ unsigned int input;
+ u32 tvaudio;
+ v4l2_std_id tvnorm;
+ unsigned int tuner_type;
+ unsigned char tuner_addr;
+ unsigned int radio_type;
+ unsigned char radio_addr;
+ unsigned int has_radio;
+
+ /* V4l */
+ u32 freq;
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ struct cx23885_dmaqueue vidq;
+ struct cx23885_dmaqueue vbiq;
+ spinlock_t slock;
};
+extern struct list_head cx23885_devlist;
+
#define SRAM_CH01 0 /* Video A */
#define SRAM_CH02 1 /* VBI A */
#define SRAM_CH03 2 /* Video B */
@@ -254,19 +358,42 @@ struct sram_channel {
#define cx_set(reg,bit) cx_andor((reg),(bit),(bit))
#define cx_clear(reg,bit) cx_andor((reg),(bit),0)
+/* ----------------------------------------------------------- */
+/* cx23885-core.c */
+
extern int cx23885_sram_channel_setup(struct cx23885_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc);
-/* ----------------------------------------------------------- */
-/* cx23885-cards.c */
+extern void cx23885_sram_channel_dump(struct cx23885_dev *dev,
+ struct sram_channel *ch);
+extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+
+extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
+void cx23885_cancel_buffers(struct cx23885_tsport *port);
+
+extern int cx23885_restart_queue(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q);
+
+extern void cx23885_wakeup(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q, u32 count);
+
+
+/* ----------------------------------------------------------- */
+/* cx23885-cards.c */
extern struct cx23885_board cx23885_boards[];
extern const unsigned int cx23885_bcount;
extern struct cx23885_subid cx23885_subids[];
extern const unsigned int cx23885_idcount;
+extern int cx23885_tuner_callback(void *priv, int command, int arg);
extern void cx23885_card_list(struct cx23885_dev *dev);
extern int cx23885_ir_init(struct cx23885_dev *dev);
extern void cx23885_gpio_setup(struct cx23885_dev *dev);
@@ -280,19 +407,50 @@ extern int cx23885_buf_prepare(struct videobuf_queue *q,
struct cx23885_tsport *port,
struct cx23885_buffer *buf,
enum v4l2_field field);
-
extern void cx23885_buf_queue(struct cx23885_tsport *port,
struct cx23885_buffer *buf);
extern void cx23885_free_buffer(struct videobuf_queue *q,
struct cx23885_buffer *buf);
/* ----------------------------------------------------------- */
+/* cx23885-video.c */
+/* Video */
+extern int cx23885_video_register(struct cx23885_dev *dev);
+extern void cx23885_video_unregister(struct cx23885_dev *dev);
+extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
+
+/* ----------------------------------------------------------- */
+/* cx23885-vbi.c */
+extern int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern void cx23885_vbi_timeout(unsigned long data);
+extern struct videobuf_queue_ops cx23885_vbi_qops;
+
/* cx23885-i2c.c */
extern int cx23885_i2c_register(struct cx23885_i2c *bus);
extern int cx23885_i2c_unregister(struct cx23885_i2c *bus);
extern void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd,
void *arg);
+/* ----------------------------------------------------------- */
+/* tv norms */
+
+static inline unsigned int norm_maxw(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+}
+
+static inline unsigned int norm_maxh(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
+static inline unsigned int norm_swidth(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+}
+
+
/*
* Local variables:
* c-basic-offset: 8
diff --git a/trunk/drivers/media/video/cx25840/cx25840-audio.c b/trunk/drivers/media/video/cx25840/cx25840-audio.c
index 3d46a776df36..d6421e1e8f6a 100644
--- a/trunk/drivers/media/video/cx25840/cx25840-audio.c
+++ b/trunk/drivers/media/video/cx25840/cx25840-audio.c
@@ -32,118 +32,156 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
/* common for all inputs and rates */
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
- cx25840_write(client, 0x127, 0x50);
+ if (!state->is_cx23885)
+ cx25840_write(client, 0x127, 0x50);
if (state->aud_input != CX25840_AUDIO_SERIAL) {
switch (freq) {
case 32000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040610);
+ cx25840_write4(client, 0x108, 0x1006040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xee39bb01);
+ cx25840_write4(client, 0x110, 0x01bb39ee);
if (state->is_cx25836)
break;
/* src3/4/6_ctl = 0x0801f77f */
- cx25840_write4(client, 0x900, 0x7ff70108);
- cx25840_write4(client, 0x904, 0x7ff70108);
- cx25840_write4(client, 0x90c, 0x7ff70108);
+ cx25840_write4(client, 0x900, 0x0801f77f);
+ cx25840_write4(client, 0x904, 0x0801f77f);
+ cx25840_write4(client, 0x90c, 0x0801f77f);
break;
case 44100:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040910);
+ cx25840_write4(client, 0x108, 0x1009040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xd66bec00);
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
if (state->is_cx25836)
break;
/* src3/4/6_ctl = 0x08016d59 */
- cx25840_write4(client, 0x900, 0x596d0108);
- cx25840_write4(client, 0x904, 0x596d0108);
- cx25840_write4(client, 0x90c, 0x596d0108);
+ cx25840_write4(client, 0x900, 0x08016d59);
+ cx25840_write4(client, 0x904, 0x08016d59);
+ cx25840_write4(client, 0x90c, 0x08016d59);
break;
case 48000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040a10);
+ cx25840_write4(client, 0x108, 0x100a040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xe5d69800);
+ cx25840_write4(client, 0x110, 0x0098d6e5);
if (state->is_cx25836)
break;
/* src3/4/6_ctl = 0x08014faa */
- cx25840_write4(client, 0x900, 0xaa4f0108);
- cx25840_write4(client, 0x904, 0xaa4f0108);
- cx25840_write4(client, 0x90c, 0xaa4f0108);
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
break;
}
} else {
switch (freq) {
case 32000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f04081e);
+ cx25840_write4(client, 0x108, 0x1e08040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x69082a01);
+ cx25840_write4(client, 0x110, 0x012a0869);
if (state->is_cx25836)
break;
/* src1_ctl = 0x08010000 */
- cx25840_write4(client, 0x8f8, 0x00000108);
+ cx25840_write4(client, 0x8f8, 0x08010000);
/* src3/4/6_ctl = 0x08020000 */
- cx25840_write4(client, 0x900, 0x00000208);
- cx25840_write4(client, 0x904, 0x00000208);
- cx25840_write4(client, 0x90c, 0x00000208);
+ cx25840_write4(client, 0x900, 0x08020000);
+ cx25840_write4(client, 0x904, 0x08020000);
+ cx25840_write4(client, 0x90c, 0x08020000);
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
cx25840_write(client, 0x127, 0x54);
break;
case 44100:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
+
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040918);
+ cx25840_write4(client, 0x108, 0x1809040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xd66bec00);
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
if (state->is_cx25836)
break;
/* src1_ctl = 0x08010000 */
- cx25840_write4(client, 0x8f8, 0xcd600108);
+ cx25840_write4(client, 0x8f8, 0x080160cd);
/* src3/4/6_ctl = 0x08020000 */
- cx25840_write4(client, 0x900, 0x85730108);
- cx25840_write4(client, 0x904, 0x85730108);
- cx25840_write4(client, 0x90c, 0x85730108);
+ cx25840_write4(client, 0x900, 0x08017385);
+ cx25840_write4(client, 0x904, 0x08017385);
+ cx25840_write4(client, 0x90c, 0x08017385);
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040a18);
+ if (!state->is_cx23885) {
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x180a040f);
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xe5d69800);
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0x0098d6e5);
+ }
if (state->is_cx25836)
break;
- /* src1_ctl = 0x08010000 */
- cx25840_write4(client, 0x8f8, 0x00800108);
+ if (!state->is_cx23885) {
+ /* src1_ctl */
+ cx25840_write4(client, 0x8f8, 0x08018000);
- /* src3/4/6_ctl = 0x08020000 */
- cx25840_write4(client, 0x900, 0x55550108);
- cx25840_write4(client, 0x904, 0x55550108);
- cx25840_write4(client, 0x90c, 0x55550108);
+ /* src3/4/6_ctl */
+ cx25840_write4(client, 0x900, 0x08015555);
+ cx25840_write4(client, 0x904, 0x08015555);
+ cx25840_write4(client, 0x90c, 0x08015555);
+ } else {
+
+ cx25840_write4(client, 0x8f8, 0x0801867c);
+
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ }
break;
}
}
@@ -168,14 +206,14 @@ void cx25840_audio_set_path(struct i2c_client *client)
if (state->aud_input == CX25840_AUDIO_SERIAL) {
/* Set Path1 to Serial Audio Input */
- cx25840_write4(client, 0x8d0, 0x12100101);
+ cx25840_write4(client, 0x8d0, 0x01011012);
/* The microcontroller should not be started for the
* non-tuner inputs: autodetection is specific for
* TV audio. */
} else {
/* Set Path1 to Analog Demod Main Channel */
- cx25840_write4(client, 0x8d0, 0x7038061f);
+ cx25840_write4(client, 0x8d0, 0x1f063870);
}
set_audclk_freq(client, state->audclk_freq);
@@ -188,6 +226,11 @@ void cx25840_audio_set_path(struct i2c_client *client)
/* deassert soft reset */
cx25840_and_or(client, 0x810, ~0x1, 0x00);
+
+ if (state->is_cx23885) {
+ /* Ensure the controller is running when we exit */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ }
}
static int get_volume(struct i2c_client *client)
diff --git a/trunk/drivers/media/video/cx25840/cx25840-core.c b/trunk/drivers/media/video/cx25840/cx25840-core.c
index 15f191e170d2..756a1eeb274e 100644
--- a/trunk/drivers/media/video/cx25840/cx25840-core.c
+++ b/trunk/drivers/media/video/cx25840/cx25840-core.c
@@ -13,6 +13,8 @@
* NTSC sliced VBI support by Christopher Neufeld
* with additional fixes by Hans Verkuil .
*
+ * CX23885 support by Steven Toth .
+ *
* 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
@@ -37,6 +39,7 @@
#include
#include
#include
+#include
#include
#include "cx25840-core.h"
@@ -72,10 +75,10 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
u8 buffer[6];
buffer[0] = addr >> 8;
buffer[1] = addr & 0xff;
- buffer[2] = value >> 24;
- buffer[3] = (value >> 16) & 0xff;
- buffer[4] = (value >> 8) & 0xff;
- buffer[5] = value & 0xff;
+ buffer[2] = value & 0xff;
+ buffer[3] = (value >> 8) & 0xff;
+ buffer[4] = (value >> 16) & 0xff;
+ buffer[5] = value >> 24;
return i2c_master_send(client, buffer, 6);
}
@@ -122,8 +125,6 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
enum cx25840_audio_input aud_input);
-static void log_audio_status(struct i2c_client *client);
-static void log_video_status(struct i2c_client *client);
/* ----------------------------------------------------------------------- */
@@ -256,6 +257,96 @@ static void cx25840_initialize(struct i2c_client *client)
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
+static void cx23885_initialize(struct i2c_client *client)
+{
+ DEFINE_WAIT(wait);
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ struct workqueue_struct *q;
+
+ /* Internal Reset */
+ cx25840_and_or(client, 0x102, ~0x01, 0x01);
+ cx25840_and_or(client, 0x102, ~0x01, 0x00);
+
+ /* Stop microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+
+ /* DIF in reset? */
+ cx25840_write(client, 0x398, 0);
+
+ /* Trust the default xtal, no division */
+ /* This changes for the cx23888 products */
+ cx25840_write(client, 0x2, 0x76);
+
+ /* Bring down the regulator for AUX clk */
+ cx25840_write(client, 0x1, 0x40);
+
+ /* Sys PLL frac */
+ cx25840_write4(client, 0x11c, 0x01d1744c);
+
+ /* Sys PLL int */
+ cx25840_write4(client, 0x118, 0x00000416);
+
+ /* Disable DIF bypass */
+ cx25840_write4(client, 0x33c, 0x00000001);
+
+ /* DIF Src phase inc */
+ cx25840_write4(client, 0x340, 0x0df7df83);
+
+ /* Vid PLL frac */
+ cx25840_write4(client, 0x10c, 0x01b6db7b);
+
+ /* Vid PLL int */
+ cx25840_write4(client, 0x108, 0x00000512);
+
+ /* Luma */
+ cx25840_write4(client, 0x414, 0x00107d12);
+
+ /* Chroma */
+ cx25840_write4(client, 0x420, 0x3d008282);
+
+ /* Aux PLL frac */
+ cx25840_write4(client, 0x114, 0x017dbf48);
+
+ /* Aux PLL int */
+ cx25840_write4(client, 0x110, 0x000a030e);
+
+ /* ADC2 input select */
+ cx25840_write(client, 0x102, 0x10);
+
+ /* VIN1 & VIN5 */
+ cx25840_write(client, 0x103, 0x11);
+
+ /* Enable format auto detect */
+ cx25840_write(client, 0x400, 0);
+ /* Fast subchroma lock */
+ /* White crush, Chroma AGC & Chroma Killer enabled */
+ cx25840_write(client, 0x401, 0xe8);
+
+ /* Select AFE clock pad output source */
+ cx25840_write(client, 0x144, 0x05);
+
+ /* Do the firmware load in a work handler to prevent.
+ Otherwise the kernel is blocked waiting for the
+ bit-banging i2c interface to finish uploading the
+ firmware. */
+ INIT_WORK(&state->fw_work, cx25840_work_handler);
+ init_waitqueue_head(&state->fw_wait);
+ q = create_singlethread_workqueue("cx25840_fw");
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+
+ cx25840_vbi_setup(client);
+
+ /* (re)set input */
+ set_input(client, state->vid_input, state->aud_input);
+
+ /* start microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+}
+
/* ----------------------------------------------------------------------- */
static void input_change(struct i2c_client *client)
@@ -319,9 +410,22 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
vid_input <= CX25840_COMPOSITE8);
u8 reg;
- v4l_dbg(1, cx25840_debug, client, "decoder set video input %d, audio input %d\n",
- vid_input, aud_input);
+ v4l_dbg(1, cx25840_debug, client,
+ "decoder set video input %d, audio input %d\n",
+ vid_input, aud_input);
+
+ if (vid_input >= CX25840_VIN1_CH1) {
+ v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n",
+ vid_input);
+ reg = vid_input & 0xff;
+ if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON)
+ is_composite = 0;
+ else
+ is_composite = 1;
+ v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n",
+ reg, is_composite);
+ } else
if (is_composite) {
reg = 0xf0 + (vid_input - CX25840_COMPOSITE1);
} else {
@@ -331,7 +435,8 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
if ((vid_input & ~0xff0) ||
luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 ||
chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) {
- v4l_err(client, "0x%04x is not a valid video input!\n", vid_input);
+ v4l_err(client, "0x%04x is not a valid video input!\n",
+ vid_input);
return -EINVAL;
}
reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4);
@@ -344,31 +449,49 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
}
}
- switch (aud_input) {
- case CX25840_AUDIO_SERIAL:
- /* do nothing, use serial audio input */
- break;
- case CX25840_AUDIO4: reg &= ~0x30; break;
- case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
- case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
- case CX25840_AUDIO7: reg &= ~0xc0; break;
- case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
+ /* The caller has previously prepared the correct routing
+ * configuration in reg (for the cx23885) so we have no
+ * need to attempt to flip bits for earlier av decoders.
+ */
+ if (!state->is_cx23885) {
+ switch (aud_input) {
+ case CX25840_AUDIO_SERIAL:
+ /* do nothing, use serial audio input */
+ break;
+ case CX25840_AUDIO4: reg &= ~0x30; break;
+ case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
+ case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
+ case CX25840_AUDIO7: reg &= ~0xc0; break;
+ case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
- default:
- v4l_err(client, "0x%04x is not a valid audio input!\n", aud_input);
- return -EINVAL;
+ default:
+ v4l_err(client, "0x%04x is not a valid audio input!\n",
+ aud_input);
+ return -EINVAL;
+ }
}
cx25840_write(client, 0x103, reg);
+
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
- /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
- cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
- /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
- if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
- cx25840_and_or(client, 0x102, ~0x4, 4);
- else
- cx25840_and_or(client, 0x102, ~0x4, 0);
+
+ if (!state->is_cx23885) {
+ /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
+ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */
+ if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
+ cx25840_and_or(client, 0x102, ~0x4, 4);
+ else
+ cx25840_and_or(client, 0x102, ~0x4, 0);
+ } else {
+ if (is_composite)
+ /* ADC2 input select channel 2 */
+ cx25840_and_or(client, 0x102, ~0x2, 0);
+ else
+ /* ADC2 input select channel 3 */
+ cx25840_and_or(client, 0x102, ~0x2, 2);
+ }
state->vid_input = vid_input;
state->aud_input = aud_input;
@@ -376,6 +499,25 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
cx25840_audio_set_path(client);
input_change(client);
}
+
+ if (state->is_cx23885) {
+ /* Audio channel 1 src : Parallel 1 */
+ cx25840_write(client, 0x124, 0x03);
+
+ /* Select AFE clock pad output source */
+ cx25840_write(client, 0x144, 0x05);
+
+ /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
+ cx25840_write(client, 0x914, 0xa0);
+
+ /* I2S_OUT_CTL:
+ * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
+ * I2S_OUT_MASTER_MODE = Master
+ */
+ cx25840_write(client, 0x918, 0xa0);
+ cx25840_write(client, 0x919, 0x01);
+ }
+
return 0;
}
@@ -641,6 +783,200 @@ static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
/* ----------------------------------------------------------------------- */
+static void log_video_status(struct i2c_client *client)
+{
+ static const char *const fmt_strs[] = {
+ "0x0",
+ "NTSC-M", "NTSC-J", "NTSC-4.43",
+ "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
+ "0x9", "0xA", "0xB",
+ "SECAM",
+ "0xD", "0xE", "0xF"
+ };
+
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
+ u8 gen_stat1 = cx25840_read(client, 0x40d);
+ u8 gen_stat2 = cx25840_read(client, 0x40e);
+ int vid_input = state->vid_input;
+
+ v4l_info(client, "Video signal: %spresent\n",
+ (gen_stat2 & 0x20) ? "" : "not ");
+ v4l_info(client, "Detected format: %s\n",
+ fmt_strs[gen_stat1 & 0xf]);
+
+ v4l_info(client, "Specified standard: %s\n",
+ vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
+
+ if (vid_input >= CX25840_COMPOSITE1 &&
+ vid_input <= CX25840_COMPOSITE8) {
+ v4l_info(client, "Specified video input: Composite %d\n",
+ vid_input - CX25840_COMPOSITE1 + 1);
+ } else {
+ v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
+ (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
+ }
+
+ v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void log_audio_status(struct i2c_client *client)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ u8 download_ctl = cx25840_read(client, 0x803);
+ u8 mod_det_stat0 = cx25840_read(client, 0x804);
+ u8 mod_det_stat1 = cx25840_read(client, 0x805);
+ u8 audio_config = cx25840_read(client, 0x808);
+ u8 pref_mode = cx25840_read(client, 0x809);
+ u8 afc0 = cx25840_read(client, 0x80b);
+ u8 mute_ctl = cx25840_read(client, 0x8d3);
+ int aud_input = state->aud_input;
+ char *p;
+
+ switch (mod_det_stat0) {
+ case 0x00: p = "mono"; break;
+ case 0x01: p = "stereo"; break;
+ case 0x02: p = "dual"; break;
+ case 0x04: p = "tri"; break;
+ case 0x10: p = "mono with SAP"; break;
+ case 0x11: p = "stereo with SAP"; break;
+ case 0x12: p = "dual with SAP"; break;
+ case 0x14: p = "tri with SAP"; break;
+ case 0xfe: p = "forced mode"; break;
+ default: p = "not defined";
+ }
+ v4l_info(client, "Detected audio mode: %s\n", p);
+
+ switch (mod_det_stat1) {
+ case 0x00: p = "not defined"; break;
+ case 0x01: p = "EIAJ"; break;
+ case 0x02: p = "A2-M"; break;
+ case 0x03: p = "A2-BG"; break;
+ case 0x04: p = "A2-DK1"; break;
+ case 0x05: p = "A2-DK2"; break;
+ case 0x06: p = "A2-DK3"; break;
+ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x08: p = "AM-L"; break;
+ case 0x09: p = "NICAM-BG"; break;
+ case 0x0a: p = "NICAM-DK"; break;
+ case 0x0b: p = "NICAM-I"; break;
+ case 0x0c: p = "NICAM-L"; break;
+ case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
+ case 0x0e: p = "IF FM Radio"; break;
+ case 0x0f: p = "BTSC"; break;
+ case 0x10: p = "high-deviation FM"; break;
+ case 0x11: p = "very high-deviation FM"; break;
+ case 0xfd: p = "unknown audio standard"; break;
+ case 0xfe: p = "forced audio standard"; break;
+ case 0xff: p = "no detected audio standard"; break;
+ default: p = "not defined";
+ }
+ v4l_info(client, "Detected audio standard: %s\n", p);
+ v4l_info(client, "Audio muted: %s\n",
+ (state->unmute_volume >= 0) ? "yes" : "no");
+ v4l_info(client, "Audio microcontroller: %s\n",
+ (download_ctl & 0x10) ?
+ ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
+
+ switch (audio_config >> 4) {
+ case 0x00: p = "undefined"; break;
+ case 0x01: p = "BTSC"; break;
+ case 0x02: p = "EIAJ"; break;
+ case 0x03: p = "A2-M"; break;
+ case 0x04: p = "A2-BG"; break;
+ case 0x05: p = "A2-DK1"; break;
+ case 0x06: p = "A2-DK2"; break;
+ case 0x07: p = "A2-DK3"; break;
+ case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x09: p = "AM-L"; break;
+ case 0x0a: p = "NICAM-BG"; break;
+ case 0x0b: p = "NICAM-DK"; break;
+ case 0x0c: p = "NICAM-I"; break;
+ case 0x0d: p = "NICAM-L"; break;
+ case 0x0e: p = "FM radio"; break;
+ case 0x0f: p = "automatic detection"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio standard: %s\n", p);
+
+ if ((audio_config >> 4) < 0xF) {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
+ case 0x01: p = "MONO2 (LANGUAGE B)"; break;
+ case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
+ case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
+ case 0x04: p = "STEREO"; break;
+ case 0x05: p = "DUAL1 (AB)"; break;
+ case 0x06: p = "DUAL2 (AC) (FM)"; break;
+ case 0x07: p = "DUAL3 (BC) (FM)"; break;
+ case 0x08: p = "DUAL4 (AC) (AM)"; break;
+ case 0x09: p = "DUAL5 (BC) (AM)"; break;
+ case 0x0a: p = "SAP"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio mode: %s\n", p);
+ } else {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "BG"; break;
+ case 0x01: p = "DK1"; break;
+ case 0x02: p = "DK2"; break;
+ case 0x03: p = "DK3"; break;
+ case 0x04: p = "I"; break;
+ case 0x05: p = "L"; break;
+ case 0x06: p = "BTSC"; break;
+ case 0x07: p = "EIAJ"; break;
+ case 0x08: p = "A2-M"; break;
+ case 0x09: p = "FM Radio"; break;
+ case 0x0f: p = "automatic standard and mode detection"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio system: %s\n", p);
+ }
+
+ if (aud_input) {
+ v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input);
+ } else {
+ v4l_info(client, "Specified audio input: External\n");
+ }
+
+ switch (pref_mode & 0xf) {
+ case 0: p = "mono/language A"; break;
+ case 1: p = "language B"; break;
+ case 2: p = "language C"; break;
+ case 3: p = "analog fallback"; break;
+ case 4: p = "stereo"; break;
+ case 5: p = "language AC"; break;
+ case 6: p = "language BC"; break;
+ case 7: p = "language AB"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Preferred audio mode: %s\n", p);
+
+ if ((audio_config & 0xf) == 0xf) {
+ switch ((afc0 >> 3) & 0x3) {
+ case 0: p = "system DK"; break;
+ case 1: p = "system L"; break;
+ case 2: p = "autodetect"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Selected 65 MHz format: %s\n", p);
+
+ switch (afc0 & 0x7) {
+ case 0: p = "chroma"; break;
+ case 1: p = "BTSC"; break;
+ case 2: p = "EIAJ"; break;
+ case 3: p = "A2-M"; break;
+ case 4: p = "autodetect"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Selected 45 MHz format: %s\n", p);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
static int cx25840_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
@@ -660,6 +996,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
state->is_initialized = 1;
if (state->is_cx25836)
cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
else
cx25840_initialize(client);
}
@@ -677,6 +1015,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
+
if (cmd == VIDIOC_DBG_G_REGISTER)
reg->val = cx25840_read(client, reg->reg & 0x0fff);
else
@@ -693,14 +1032,26 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
case VIDIOC_STREAMON:
v4l_dbg(1, cx25840_debug, client, "enable output\n");
- cx25840_write(client, 0x115, state->is_cx25836 ? 0x0c : 0x8c);
- cx25840_write(client, 0x116, state->is_cx25836 ? 0x04 : 0x07);
+ if (state->is_cx23885) {
+ u8 v = (cx25840_read(client, 0x421) | 0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+ cx25840_write(client, 0x115,
+ state->is_cx25836 ? 0x0c : 0x8c);
+ cx25840_write(client, 0x116,
+ state->is_cx25836 ? 0x04 : 0x07);
+ }
break;
case VIDIOC_STREAMOFF:
v4l_dbg(1, cx25840_debug, client, "disable output\n");
- cx25840_write(client, 0x115, 0x00);
- cx25840_write(client, 0x116, 0x00);
+ if (state->is_cx23885) {
+ u8 v = cx25840_read(client, 0x421) & ~(0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+ cx25840_write(client, 0x115, 0x00);
+ cx25840_write(client, 0x116, 0x00);
+ }
break;
case VIDIOC_LOG_STATUS:
@@ -863,6 +1214,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
case VIDIOC_INT_RESET:
if (state->is_cx25836)
cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
else
cx25840_initialize(client);
break;
@@ -879,35 +1232,21 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
/* ----------------------------------------------------------------------- */
-static struct i2c_driver i2c_driver_cx25840;
-
-static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
- int kind)
+static int cx25840_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct cx25840_state *state;
u32 id;
u16 device_id;
- /* Check if the adapter supports the needed features
- * Not until kernel version 2.6.11 did the bit-algo
- * correctly report that it would do an I2C-level xfer */
- if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
-
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver_cx25840;
- snprintf(client->name, sizeof(client->name) - 1, "cx25840");
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1);
device_id = cx25840_read(client, 0x101) << 8;
device_id |= cx25840_read(client, 0x100);
+ v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id);
/* The high byte of the device ID should be
* 0x83 for the cx2583x and 0x84 for the cx2584x */
@@ -916,16 +1255,18 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
}
else if ((device_id & 0xff00) == 0x8400) {
id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
+ } else if (device_id == 0x0000) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ } else if (device_id == 0x1313) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
}
else {
v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
- kfree(client);
- return 0;
+ return -ENODEV;
}
state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL);
if (state == NULL) {
- kfree(client);
return -ENOMEM;
}
@@ -939,6 +1280,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
i2c_set_clientdata(client, state);
state->c = client;
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
+ state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
state->vid_input = CX25840_COMPOSITE7;
state->aud_input = CX25840_AUDIO8;
state->audclk_freq = 48000;
@@ -949,250 +1291,19 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
state->id = id;
state->rev = device_id;
- i2c_attach_client(client);
-
- return 0;
-}
-
-static int cx25840_attach_adapter(struct i2c_adapter *adapter)
-{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, &cx25840_detect_client);
return 0;
}
-static int cx25840_detach_client(struct i2c_client *client)
+static int cx25840_remove(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
-
- kfree(state);
- kfree(client);
-
+ kfree(i2c_get_clientdata(client));
return 0;
}
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_driver i2c_driver_cx25840 = {
- .driver = {
- .name = "cx25840",
- },
- .id = I2C_DRIVERID_CX25840,
- .attach_adapter = cx25840_attach_adapter,
- .detach_client = cx25840_detach_client,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cx25840",
+ .driverid = I2C_DRIVERID_CX25840,
.command = cx25840_command,
+ .probe = cx25840_probe,
+ .remove = cx25840_remove,
};
-
-
-static int __init m__init(void)
-{
- return i2c_add_driver(&i2c_driver_cx25840);
-}
-
-static void __exit m__exit(void)
-{
- i2c_del_driver(&i2c_driver_cx25840);
-}
-
-module_init(m__init);
-module_exit(m__exit);
-
-/* ----------------------------------------------------------------------- */
-
-static void log_video_status(struct i2c_client *client)
-{
- static const char *const fmt_strs[] = {
- "0x0",
- "NTSC-M", "NTSC-J", "NTSC-4.43",
- "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
- "0x9", "0xA", "0xB",
- "SECAM",
- "0xD", "0xE", "0xF"
- };
-
- struct cx25840_state *state = i2c_get_clientdata(client);
- u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
- u8 gen_stat1 = cx25840_read(client, 0x40d);
- u8 gen_stat2 = cx25840_read(client, 0x40e);
- int vid_input = state->vid_input;
-
- v4l_info(client, "Video signal: %spresent\n",
- (gen_stat2 & 0x20) ? "" : "not ");
- v4l_info(client, "Detected format: %s\n",
- fmt_strs[gen_stat1 & 0xf]);
-
- v4l_info(client, "Specified standard: %s\n",
- vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
-
- if (vid_input >= CX25840_COMPOSITE1 &&
- vid_input <= CX25840_COMPOSITE8) {
- v4l_info(client, "Specified video input: Composite %d\n",
- vid_input - CX25840_COMPOSITE1 + 1);
- } else {
- v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
- (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
- }
-
- v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static void log_audio_status(struct i2c_client *client)
-{
- struct cx25840_state *state = i2c_get_clientdata(client);
- u8 download_ctl = cx25840_read(client, 0x803);
- u8 mod_det_stat0 = cx25840_read(client, 0x804);
- u8 mod_det_stat1 = cx25840_read(client, 0x805);
- u8 audio_config = cx25840_read(client, 0x808);
- u8 pref_mode = cx25840_read(client, 0x809);
- u8 afc0 = cx25840_read(client, 0x80b);
- u8 mute_ctl = cx25840_read(client, 0x8d3);
- int aud_input = state->aud_input;
- char *p;
-
- switch (mod_det_stat0) {
- case 0x00: p = "mono"; break;
- case 0x01: p = "stereo"; break;
- case 0x02: p = "dual"; break;
- case 0x04: p = "tri"; break;
- case 0x10: p = "mono with SAP"; break;
- case 0x11: p = "stereo with SAP"; break;
- case 0x12: p = "dual with SAP"; break;
- case 0x14: p = "tri with SAP"; break;
- case 0xfe: p = "forced mode"; break;
- default: p = "not defined";
- }
- v4l_info(client, "Detected audio mode: %s\n", p);
-
- switch (mod_det_stat1) {
- case 0x00: p = "not defined"; break;
- case 0x01: p = "EIAJ"; break;
- case 0x02: p = "A2-M"; break;
- case 0x03: p = "A2-BG"; break;
- case 0x04: p = "A2-DK1"; break;
- case 0x05: p = "A2-DK2"; break;
- case 0x06: p = "A2-DK3"; break;
- case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
- case 0x08: p = "AM-L"; break;
- case 0x09: p = "NICAM-BG"; break;
- case 0x0a: p = "NICAM-DK"; break;
- case 0x0b: p = "NICAM-I"; break;
- case 0x0c: p = "NICAM-L"; break;
- case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
- case 0x0e: p = "IF FM Radio"; break;
- case 0x0f: p = "BTSC"; break;
- case 0x10: p = "high-deviation FM"; break;
- case 0x11: p = "very high-deviation FM"; break;
- case 0xfd: p = "unknown audio standard"; break;
- case 0xfe: p = "forced audio standard"; break;
- case 0xff: p = "no detected audio standard"; break;
- default: p = "not defined";
- }
- v4l_info(client, "Detected audio standard: %s\n", p);
- v4l_info(client, "Audio muted: %s\n",
- (state->unmute_volume >= 0) ? "yes" : "no");
- v4l_info(client, "Audio microcontroller: %s\n",
- (download_ctl & 0x10) ?
- ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
-
- switch (audio_config >> 4) {
- case 0x00: p = "undefined"; break;
- case 0x01: p = "BTSC"; break;
- case 0x02: p = "EIAJ"; break;
- case 0x03: p = "A2-M"; break;
- case 0x04: p = "A2-BG"; break;
- case 0x05: p = "A2-DK1"; break;
- case 0x06: p = "A2-DK2"; break;
- case 0x07: p = "A2-DK3"; break;
- case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
- case 0x09: p = "AM-L"; break;
- case 0x0a: p = "NICAM-BG"; break;
- case 0x0b: p = "NICAM-DK"; break;
- case 0x0c: p = "NICAM-I"; break;
- case 0x0d: p = "NICAM-L"; break;
- case 0x0e: p = "FM radio"; break;
- case 0x0f: p = "automatic detection"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Configured audio standard: %s\n", p);
-
- if ((audio_config >> 4) < 0xF) {
- switch (audio_config & 0xF) {
- case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
- case 0x01: p = "MONO2 (LANGUAGE B)"; break;
- case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
- case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
- case 0x04: p = "STEREO"; break;
- case 0x05: p = "DUAL1 (AB)"; break;
- case 0x06: p = "DUAL2 (AC) (FM)"; break;
- case 0x07: p = "DUAL3 (BC) (FM)"; break;
- case 0x08: p = "DUAL4 (AC) (AM)"; break;
- case 0x09: p = "DUAL5 (BC) (AM)"; break;
- case 0x0a: p = "SAP"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Configured audio mode: %s\n", p);
- } else {
- switch (audio_config & 0xF) {
- case 0x00: p = "BG"; break;
- case 0x01: p = "DK1"; break;
- case 0x02: p = "DK2"; break;
- case 0x03: p = "DK3"; break;
- case 0x04: p = "I"; break;
- case 0x05: p = "L"; break;
- case 0x06: p = "BTSC"; break;
- case 0x07: p = "EIAJ"; break;
- case 0x08: p = "A2-M"; break;
- case 0x09: p = "FM Radio"; break;
- case 0x0f: p = "automatic standard and mode detection"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Configured audio system: %s\n", p);
- }
-
- if (aud_input) {
- v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input);
- } else {
- v4l_info(client, "Specified audio input: External\n");
- }
-
- switch (pref_mode & 0xf) {
- case 0: p = "mono/language A"; break;
- case 1: p = "language B"; break;
- case 2: p = "language C"; break;
- case 3: p = "analog fallback"; break;
- case 4: p = "stereo"; break;
- case 5: p = "language AC"; break;
- case 6: p = "language BC"; break;
- case 7: p = "language AB"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Preferred audio mode: %s\n", p);
-
- if ((audio_config & 0xf) == 0xf) {
- switch ((afc0 >> 3) & 0x3) {
- case 0: p = "system DK"; break;
- case 1: p = "system L"; break;
- case 2: p = "autodetect"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Selected 65 MHz format: %s\n", p);
-
- switch (afc0 & 0x7) {
- case 0: p = "chroma"; break;
- case 1: p = "BTSC"; break;
- case 2: p = "EIAJ"; break;
- case 3: p = "A2-M"; break;
- case 4: p = "autodetect"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Selected 45 MHz format: %s\n", p);
- }
-}
diff --git a/trunk/drivers/media/video/cx25840/cx25840-core.h b/trunk/drivers/media/video/cx25840/cx25840-core.h
index ea669b1f084d..95093edc9186 100644
--- a/trunk/drivers/media/video/cx25840/cx25840-core.h
+++ b/trunk/drivers/media/video/cx25840/cx25840-core.h
@@ -47,6 +47,7 @@ struct cx25840_state {
u32 id;
u32 rev;
int is_cx25836;
+ int is_cx23885;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
diff --git a/trunk/drivers/media/video/cx25840/cx25840-firmware.c b/trunk/drivers/media/video/cx25840/cx25840-firmware.c
index e852024a5ea3..1ddf724a2c74 100644
--- a/trunk/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/trunk/drivers/media/video/cx25840/cx25840-firmware.c
@@ -24,6 +24,7 @@
#include "cx25840-core.h"
#define FWFILE "v4l-cx25840.fw"
+#define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw"
/*
* Mike Isely - The FWSEND parameter controls the
@@ -92,10 +93,14 @@ static int fw_write(struct i2c_client *client, u8 * data, int size)
int cx25840_loadfw(struct i2c_client *client)
{
+ struct cx25840_state *state = i2c_get_clientdata(client);
const struct firmware *fw = NULL;
u8 buffer[4], *ptr;
int size, send, retval;
+ if (state->is_cx23885)
+ firmware = FWFILE_CX23885;
+
if (request_firmware(&fw, firmware, FWDEV(client)) != 0) {
v4l_err(client, "unable to open firmware %s\n", firmware);
return -EINVAL;
diff --git a/trunk/drivers/media/video/cx25840/cx25840-vbi.c b/trunk/drivers/media/video/cx25840/cx25840-vbi.c
index ced13febed89..6828f59b9d83 100644
--- a/trunk/drivers/media/video/cx25840/cx25840-vbi.c
+++ b/trunk/drivers/media/video/cx25840/cx25840-vbi.c
@@ -180,7 +180,7 @@ void cx25840_vbi_setup(struct i2c_client *client)
fsc/1000000,fsc%1000000);
v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
- "vblank %i , vactive %i, vblank656 %i, src_dec %i,"
+ "vblank %i, vactive %i, vblank656 %i, src_dec %i, "
"burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
" sc 0x%06x\n",
hblank, hactive, vblank, vactive, vblank656,
diff --git a/trunk/drivers/media/video/cx88/Kconfig b/trunk/drivers/media/video/cx88/Kconfig
index ceb31d4a2512..49d3813a9b48 100644
--- a/trunk/drivers/media/video/cx88/Kconfig
+++ b/trunk/drivers/media/video/cx88/Kconfig
@@ -8,6 +8,7 @@ config VIDEO_CX88
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
+ select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO
---help---
This is a video4linux driver for Conexant 2388x based
TV cards.
diff --git a/trunk/drivers/media/video/cx88/cx88-alsa.c b/trunk/drivers/media/video/cx88/cx88-alsa.c
index 40ffd7a5579a..8735227f7e47 100644
--- a/trunk/drivers/media/video/cx88/cx88-alsa.c
+++ b/trunk/drivers/media/video/cx88/cx88-alsa.c
@@ -417,7 +417,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
chip->buf = buf;
chip->dma_risc = dma;
diff --git a/trunk/drivers/media/video/cx88/cx88-blackbird.c b/trunk/drivers/media/video/cx88/cx88-blackbird.c
index f802b5653569..a99e9d5950aa 100644
--- a/trunk/drivers/media/video/cx88/cx88-blackbird.c
+++ b/trunk/drivers/media/video/cx88/cx88-blackbird.c
@@ -307,7 +307,7 @@ static int register_read(struct cx88_core *core, u32 address, u32 *value)
/* ------------------------------------------------------------------ */
-static int blackbird_mbox_func(void *priv, int command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+static int blackbird_mbox_func(void *priv, u32 command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
{
struct cx8802_dev *dev = priv;
unsigned long timeout;
@@ -536,11 +536,12 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev)
dprintk(1,"Initialize codec\n");
retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
if (retval < 0) {
+
+ dev->mpeg_active = 0;
+
/* ping was not successful, reset and upload firmware */
cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */
- msleep(1);
cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
- msleep(1);
retval = blackbird_load_firmware(dev);
if (retval < 0)
return retval;
@@ -562,7 +563,6 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev)
}
dprintk(0, "Firmware version is 0x%08x\n", version);
}
- msleep(1);
cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */
cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */
@@ -570,40 +570,68 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev)
cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */
blackbird_codec_settings(dev);
- msleep(1);
- /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef);
- blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0);
- blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180); */
blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
BLACKBIRD_FIELD1_SAA7115,
BLACKBIRD_FIELD2_SAA7115
);
- /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); */
blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
- /* initialize the video input */
- blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+ return 0;
+}
- msleep(1);
+static int blackbird_start_codec(struct file *file, void *priv)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+ /* start capturing to the host interface */
+ u32 reg;
- blackbird_api_cmd(dev, CX2341X_ENC_MUTE_VIDEO, 1, 0, BLACKBIRD_UNMUTE);
- msleep(1);
- blackbird_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, BLACKBIRD_UNMUTE);
- msleep(1);
+ int i;
+ int lastchange = -1;
+ int lastval = 0;
+
+ for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) {
+ reg = cx_read(AUD_STATUS);
+
+ dprintk(1, "AUD_STATUS:%dL: 0x%x\n", i, reg);
+ if ((reg & 0x0F) != lastval) {
+ lastval = reg & 0x0F;
+ lastchange = i;
+ }
+ msleep(100);
+ }
+
+ /* unmute audio source */
+ cx_clear(AUD_VOL_CTL, (1 << 6));
+
+ blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0, 0);
+
+ /* initialize the video input */
+ blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
/* start capturing to the host interface */
- /* blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, 0, 0x13); */
blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
BLACKBIRD_MPEG_CAPTURE,
BLACKBIRD_RAW_BITS_NONE
);
- msleep(10);
- blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0,0);
+ dev->mpeg_active = 1;
+ return 0;
+}
+
+static int blackbird_stop_codec(struct cx8802_dev *dev)
+{
+ blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+ BLACKBIRD_END_NOW,
+ BLACKBIRD_MPEG_CAPTURE,
+ BLACKBIRD_RAW_BITS_NONE
+ );
+
+ dev->mpeg_active = 0;
return 0;
}
@@ -833,6 +861,10 @@ static int vidioc_s_ext_ctrls (struct file *file, void *priv,
if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
return -EINVAL;
+
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
+
p = dev->params;
err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
if (!err) {
@@ -864,10 +896,9 @@ static int vidioc_s_frequency (struct file *file, void *priv,
struct cx8802_dev *dev = fh->dev;
struct cx88_core *core = dev->core;
- blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
- BLACKBIRD_END_NOW,
- BLACKBIRD_MPEG_CAPTURE,
- BLACKBIRD_RAW_BITS_NONE);
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
+
cx88_set_freq (core,f);
blackbird_initialize_codec(dev);
cx88_set_scale(dev->core, dev->width, dev->height,
@@ -1073,15 +1104,11 @@ static int mpeg_open(struct inode *inode, struct file *file)
static int mpeg_release(struct inode *inode, struct file *file)
{
struct cx8802_fh *fh = file->private_data;
- struct cx8802_dev *dev = NULL;
+ struct cx8802_dev *dev = fh->dev;
struct cx8802_driver *drv = NULL;
- /* blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */
- blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
- BLACKBIRD_END_NOW,
- BLACKBIRD_MPEG_CAPTURE,
- BLACKBIRD_RAW_BITS_NONE
- );
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
cx8802_cancel_buffers(fh->dev);
/* stop mpeg capture */
@@ -1107,6 +1134,10 @@ static ssize_t
mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (!dev->mpeg_active)
+ blackbird_start_codec(file, fh);
return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
file->f_flags & O_NONBLOCK);
@@ -1282,6 +1313,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
core->name);
host_setup(dev->core);
+ blackbird_initialize_codec(dev);
blackbird_register_video(dev);
/* initial device configuration: needed ? */
diff --git a/trunk/drivers/media/video/cx88/cx88-cards.c b/trunk/drivers/media/video/cx88/cx88-cards.c
index a4eb6a87a761..e6b7f518c56e 100644
--- a/trunk/drivers/media/video/cx88/cx88-cards.c
+++ b/trunk/drivers/media/video/cx88/cx88-cards.c
@@ -26,6 +26,7 @@
#include
#include "cx88.h"
+#include "tea5767.h"
static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
@@ -245,6 +246,10 @@ static const struct cx88_board cx88_boards[] = {
}},
.radio = {
.type = CX88_RADIO,
+ .vmux = 3,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff20,
},
},
[CX88_BOARD_WINFAST_DV2000] = {
@@ -297,22 +302,22 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x0000bde2,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x0000bde6,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x0000bde6,
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x0000bd62,
- .extadc = 1,
+ .audioroute = 1,
},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -373,7 +378,7 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
@@ -544,7 +549,7 @@ static const struct cx88_board cx88_boards[] = {
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
- .extadc = 1,
+ .audioroute = 1,
}},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -667,22 +672,22 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x00009d80,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x00009d76,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x00009d76,
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x00009d00,
- .extadc = 1,
+ .audioroute = 1,
},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -821,23 +826,23 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_COMPOSITE1,
.vmux = 0,
.gpio0 = 0x0000cd73,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 1,
.gpio0 = 0x0000cd73,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_TELEVISION,
.vmux = 3,
.gpio0 = 0x0000cdb3,
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
.vmux = 2,
.gpio0 = 0x0000cdf3,
- .extadc = 1,
+ .audioroute = 1,
},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -1105,12 +1110,12 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x3de6,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x3de6,
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
@@ -1335,17 +1340,17 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0xe780,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0xe780,
- .extadc = 1,
+ .audioroute = 2,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0xe780,
- .extadc = 1,
+ .audioroute = 2,
}},
/* fixme: Add radio support */
.mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
@@ -1370,6 +1375,32 @@ static const struct cx88_board cx88_boards[] = {
.gpio0 = 0x07fa,
}},
},
+ [CX88_BOARD_PINNACLE_PCTV_HD_800i] = {
+ .name = "Pinnacle PCTV HD 800i",
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ef,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ef,
+ .audioroute = 1,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
};
/* ------------------------------------------------------------------ */
@@ -1679,6 +1710,10 @@ static const struct cx88_subid cx88_subids[] = {
.subvendor = 0x1421,
.subdevice = 0x0390,
.card = CX88_BOARD_ADSTECH_PTV_390,
+ },{
+ .subvendor = 0x11bd,
+ .subdevice = 0x0051,
+ .card = CX88_BOARD_PINNACLE_PCTV_HD_800i,
},
};
@@ -1845,6 +1880,36 @@ static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core)
}
}
+/* ----------------------------------------------------------------------- */
+/* Tuner callback function. Currently only needed for the Pinnacle *
+ * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both *
+ * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c) */
+
+int cx88_tuner_callback(void *priv, int command, int arg)
+{
+ struct i2c_algo_bit_data *i2c_algo = priv;
+ struct cx88_core *core = i2c_algo->data;
+
+ switch(core->boardnr) {
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ if(command == 0) { /* This is the reset command from xc5000 */
+ /* Reset XC5000 tuner via SYS_RSTO_pin */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_SRST_IO, 1);
+ return 0;
+ }
+ else {
+ printk(KERN_ERR
+ "xc5000: unknown tuner callback command.\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0; /* Should never be here */
+}
+EXPORT_SYMBOL(cx88_tuner_callback);
+
/* ----------------------------------------------------------------------- */
static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
@@ -1979,6 +2044,23 @@ static void cx88_card_setup(struct cx88_core *core)
core->name, i);
}
break;
+ case CX88_BOARD_MSI_TVANYWHERE_MASTER:
+ {
+ struct v4l2_priv_tun_config tea5767_cfg;
+ struct tea5767_ctrl ctl;
+
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.high_cut = 1;
+ ctl.st_noise = 1;
+ ctl.deemph_75 = 1;
+ ctl.xtal_freq = TEA5767_HIGH_LO_13MHz;
+
+ tea5767_cfg.tuner = TUNER_TEA5767;
+ tea5767_cfg.priv = &ctl;
+
+ cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg);
+ }
}
}
diff --git a/trunk/drivers/media/video/cx88/cx88-core.c b/trunk/drivers/media/video/cx88/cx88-core.c
index 62e8dd24c5f5..01e2ac98970b 100644
--- a/trunk/drivers/media/video/cx88/cx88-core.c
+++ b/trunk/drivers/media/video/cx88/cx88-core.c
@@ -220,7 +220,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
videobuf_dma_unmap(q, dma);
videobuf_dma_free(dma);
btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
/* ------------------------------------------------------------------ */
@@ -538,7 +538,7 @@ void cx88_wakeup(struct cx88_core *core,
do_gettimeofday(&buf->vb.ts);
dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
count, buf->count);
- buf->vb.state = STATE_DONE;
+ buf->vb.state = VIDEOBUF_DONE;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
diff --git a/trunk/drivers/media/video/cx88/cx88-dvb.c b/trunk/drivers/media/video/cx88/cx88-dvb.c
index fce19caf9d04..f7b41eb1bb5a 100644
--- a/trunk/drivers/media/video/cx88/cx88-dvb.c
+++ b/trunk/drivers/media/video/cx88/cx88-dvb.c
@@ -40,6 +40,8 @@
#include "cx22702.h"
#include "or51132.h"
#include "lgdt330x.h"
+#include "s5h1409.h"
+#include "xc5000.h"
#include "nxt200x.h"
#include "cx24123.h"
#include "isl6421.h"
@@ -371,6 +373,22 @@ static struct cx24123_config kworld_dvbs_100_config = {
.lnb_polarity = 1,
};
+static struct s5h1409_config pinnacle_pctv_hd_800i_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+ .tuner_callback = cx88_tuner_callback,
+};
+
static int dvb_register(struct cx8802_dev *dev)
{
/* init struct videobuf_dvb */
@@ -625,6 +643,21 @@ static int dvb_register(struct cx8802_dev *dev)
dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
}
break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ dev->dvb.frontend = dvb_attach(s5h1409_attach,
+ &pinnacle_pctv_hd_800i_config,
+ &dev->core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ /* tuner_config.video_dev must point to
+ * i2c_adap.algo_data
+ */
+ pinnacle_pctv_hd_800i_tuner_config.priv =
+ dev->core->i2c_adap.algo_data;
+ dvb_attach(xc5000_attach, dev->dvb.frontend,
+ &dev->core->i2c_adap,
+ &pinnacle_pctv_hd_800i_tuner_config);
+ }
+ break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->core->name);
diff --git a/trunk/drivers/media/video/cx88/cx88-i2c.c b/trunk/drivers/media/video/cx88/cx88-i2c.c
index c8b1c50625f4..566b26af523e 100644
--- a/trunk/drivers/media/video/cx88/cx88-i2c.c
+++ b/trunk/drivers/media/video/cx88/cx88-i2c.c
@@ -109,26 +109,32 @@ static int attach_inform(struct i2c_client *client)
if (core->board.radio_type != UNSET) {
if ((core->board.radio_addr==ADDR_UNSET)||(core->board.radio_addr==client->addr)) {
- tun_setup.mode_mask = T_RADIO;
- tun_setup.type = core->board.radio_type;
- tun_setup.addr = core->board.radio_addr;
-
+ tun_setup.mode_mask = T_RADIO;
+ tun_setup.type = core->board.radio_type;
+ tun_setup.addr = core->board.radio_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
client->driver->command (client, TUNER_SET_TYPE_ADDR, &tun_setup);
}
}
if (core->board.tuner_type != UNSET) {
if ((core->board.tuner_addr==ADDR_UNSET)||(core->board.tuner_addr==client->addr)) {
- tun_setup.mode_mask = T_ANALOG_TV;
- tun_setup.type = core->board.tuner_type;
- tun_setup.addr = core->board.tuner_addr;
-
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = core->board.tuner_type;
+ tun_setup.addr = core->board.tuner_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
client->driver->command (client,TUNER_SET_TYPE_ADDR, &tun_setup);
}
}
- if (core->board.tda9887_conf)
- client->driver->command(client, TDA9887_SET_CONFIG, &core->board.tda9887_conf);
+ if (core->board.tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &core->board.tda9887_conf;
+
+ client->driver->command(client, TUNER_SET_CONFIG, &tda9887_cfg);
+ }
return 0;
}
@@ -176,6 +182,7 @@ static char *i2c_devs[128] = {
[ 0xa0 >> 1 ] = "eeprom",
[ 0xc0 >> 1 ] = "tuner (analog)",
[ 0xc2 >> 1 ] = "tuner (analog/dvb)",
+ [ 0xc8 >> 1 ] = "xc5000",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
diff --git a/trunk/drivers/media/video/cx88/cx88-input.c b/trunk/drivers/media/video/cx88/cx88-input.c
index e52de3968c63..bb0911b4d2f6 100644
--- a/trunk/drivers/media/video/cx88/cx88-input.c
+++ b/trunk/drivers/media/video/cx88/cx88-input.c
@@ -305,6 +305,11 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir->mask_keycode = 0xfa;
ir->polling = 50; /* ms */
break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ ir_codes = ir_codes_pinnacle_pctv_hd;
+ ir_type = IR_TYPE_RC5;
+ ir->sampling = 1;
+ break;
}
if (NULL == ir_codes) {
@@ -443,6 +448,7 @@ void cx88_ir_irq(struct cx88_core *core)
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
ir_dprintk("biphase decoded: %x\n", ircode);
if ((ircode & 0xfffff000) != 0x3000)
diff --git a/trunk/drivers/media/video/cx88/cx88-mpeg.c b/trunk/drivers/media/video/cx88/cx88-mpeg.c
index 448c67380945..0aedbeaf94cd 100644
--- a/trunk/drivers/media/video/cx88/cx88-mpeg.c
+++ b/trunk/drivers/media/video/cx88/cx88-mpeg.c
@@ -102,7 +102,7 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
udelay(100);
cx_write(MO_PINMUX_IO, 0x00);
- cx_write(TS_HW_SOP_CNTRL,0x47<<16|188<<4|0x01);
+ cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01);
switch (core->boardnr) {
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
@@ -117,6 +117,15 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
break;
case CX88_BOARD_HAUPPAUGE_HVR1300:
break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ /* Enable MPEG parallel IO and video signal pins */
+ cx_write(MO_PINMUX_IO, 0x88);
+ cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4));
+ dev->ts_gen_cntrl = 5;
+ cx_write(TS_SOP_STAT, 0);
+ cx_write(TS_VALERR_CNTRL, 0);
+ udelay(100);
+ break;
default:
cx_write(TS_SOP_STAT, 0x00);
break;
@@ -195,7 +204,7 @@ static int cx8802_restart_queue(struct cx8802_dev *dev,
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
cx8802_start_dma(dev, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(1,"[%p/%d] restart_queue - first active\n",
@@ -206,7 +215,7 @@ static int cx8802_restart_queue(struct cx8802_dev *dev,
prev->fmt == buf->fmt) {
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(1,"[%p/%d] restart_queue - move to active\n",
@@ -242,7 +251,7 @@ int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
buf->vb.width = dev->ts_packet_size;
buf->vb.height = dev->ts_packet_count;
buf->vb.size = size;
@@ -254,7 +263,7 @@ int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
dma->sglist,
buf->vb.width, buf->vb.height, 0);
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -276,7 +285,7 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
dprintk( 1, "queue is empty - first active\n" );
list_add_tail(&buf->vb.queue,&cx88q->active);
cx8802_start_dma(dev, cx88q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(1,"[%p/%d] %s - first active\n",
@@ -286,7 +295,7 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
dprintk( 1, "queue is not empty - append to active\n" );
prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue);
list_add_tail(&buf->vb.queue,&cx88q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk( 1, "[%p/%d] %s - append to active\n",
@@ -306,7 +315,7 @@ static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart)
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
@@ -437,10 +446,7 @@ static irqreturn_t cx8802_irq(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-/* ----------------------------------------------------------- */
-/* exported stuff */
-
-int cx8802_init_common(struct cx8802_dev *dev)
+static int cx8802_init_common(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
int err;
@@ -488,7 +494,7 @@ int cx8802_init_common(struct cx8802_dev *dev)
return 0;
}
-void cx8802_fini_common(struct cx8802_dev *dev)
+static void cx8802_fini_common(struct cx8802_dev *dev)
{
dprintk( 2, "cx8802_fini_common\n" );
cx8802_stop_dma(dev);
@@ -504,7 +510,7 @@ void cx8802_fini_common(struct cx8802_dev *dev)
/* ----------------------------------------------------------- */
-int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
+static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
{
struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
struct cx88_core *core = dev->core;
@@ -530,7 +536,7 @@ int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
return 0;
}
-int cx8802_resume_common(struct pci_dev *pci_dev)
+static int cx8802_resume_common(struct pci_dev *pci_dev)
{
struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
struct cx88_core *core = dev->core;
@@ -874,9 +880,6 @@ EXPORT_SYMBOL(cx8802_buf_prepare);
EXPORT_SYMBOL(cx8802_buf_queue);
EXPORT_SYMBOL(cx8802_cancel_buffers);
-EXPORT_SYMBOL(cx8802_init_common);
-EXPORT_SYMBOL(cx8802_fini_common);
-
EXPORT_SYMBOL(cx8802_register_driver);
EXPORT_SYMBOL(cx8802_unregister_driver);
EXPORT_SYMBOL(cx8802_get_driver);
diff --git a/trunk/drivers/media/video/cx88/cx88-vbi.c b/trunk/drivers/media/video/cx88/cx88-vbi.c
index babb08556406..d96ecfcf393a 100644
--- a/trunk/drivers/media/video/cx88/cx88-vbi.c
+++ b/trunk/drivers/media/video/cx88/cx88-vbi.c
@@ -130,7 +130,7 @@ void cx8800_vbi_timeout(unsigned long data)
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name,
buf, buf->vb.i, (unsigned long)buf->risc.dma);
@@ -168,7 +168,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
buf->vb.width = VBI_LINE_LENGTH;
buf->vb.height = VBI_LINE_COUNT;
@@ -183,7 +183,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
buf->vb.width, 0,
buf->vb.height);
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -207,7 +207,7 @@ vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue,&q->active);
cx8800_start_vbi_dma(dev, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] vbi_queue - first active\n",
@@ -216,7 +216,7 @@ vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
} else {
prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(2,"[%p/%d] buffer_queue - append to active\n",
diff --git a/trunk/drivers/media/video/cx88/cx88-video.c b/trunk/drivers/media/video/cx88/cx88-video.c
index c84dafbdb991..7f1931aed207 100644
--- a/trunk/drivers/media/video/cx88/cx88-video.c
+++ b/trunk/drivers/media/video/cx88/cx88-video.c
@@ -392,13 +392,41 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input)
break;
}
- if (core->board.mpeg & CX88_MPEG_BLACKBIRD) {
- /* sets sound input from external adc */
- if (INPUT(input).extadc)
+ /* if there are audioroutes defined, we have an external
+ ADC to deal with audio */
+
+ if (INPUT(input).audioroute) {
+
+ /* cx2388's C-ADC is connected to the tuner only.
+ When used with S-Video, that ADC is busy dealing with
+ chroma, so an external must be used for baseband audio */
+
+ if (INPUT(input).type != CX88_VMUX_TELEVISION &&
+ INPUT(input).type != CX88_RADIO) {
+ /* "ADC mode" */
+ cx_write(AUD_I2SCNTL, 0x1);
cx_set(AUD_CTL, EN_I2SIN_ENABLE);
- else
+ } else {
+ /* Normal mode */
+ cx_write(AUD_I2SCNTL, 0x0);
cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
+ }
+
+ /* The wm8775 module has the "2" route hardwired into
+ the initialization. Some boards may use different
+ routes for different inputs. HVR-1300 surely does */
+ if (core->board.audio_chip &&
+ core->board.audio_chip == AUDIO_CHIP_WM8775) {
+ struct v4l2_routing route;
+
+ route.input = INPUT(input).audioroute;
+ cx88_call_i2c_clients(core,
+ VIDIOC_INT_S_AUDIO_ROUTING, &route);
+
+ }
+
}
+
return 0;
}
EXPORT_SYMBOL(cx88_video_mux);
@@ -486,7 +514,7 @@ static int restart_video_queue(struct cx8800_dev *dev,
if (NULL == prev) {
list_move_tail(&buf->vb.queue, &q->active);
start_video_dma(dev, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] restart_queue - first active\n",
@@ -496,7 +524,7 @@ static int restart_video_queue(struct cx8800_dev *dev,
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
list_move_tail(&buf->vb.queue, &q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(2,"[%p/%d] restart_queue - move to active\n",
@@ -553,7 +581,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
init_buffer = 1;
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
init_buffer = 1;
if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
goto fail;
@@ -601,7 +629,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
(unsigned long)buf->risc.dma);
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -625,14 +653,14 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue,&q->queued);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2,"[%p/%d] buffer_queue - append to queued\n",
buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue,&q->active);
start_video_dma(dev, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] buffer_queue - first active\n",
@@ -644,7 +672,7 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(2,"[%p/%d] buffer_queue - append to active\n",
@@ -652,7 +680,7 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
} else {
list_add_tail(&buf->vb.queue,&q->queued);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2,"[%p/%d] buffer_queue - first queued\n",
buf, buf->vb.i);
}
@@ -822,8 +850,8 @@ video_poll(struct file *file, struct poll_table_struct *wait)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == STATE_DONE ||
- buf->vb.state == STATE_ERROR)
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
@@ -1496,7 +1524,7 @@ static void cx8800_vid_timeout(unsigned long data)
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name,
buf, buf->vb.i, (unsigned long)buf->risc.dma);
diff --git a/trunk/drivers/media/video/cx88/cx88.h b/trunk/drivers/media/video/cx88/cx88.h
index eb296bdecb1e..4e823f2a539a 100644
--- a/trunk/drivers/media/video/cx88/cx88.h
+++ b/trunk/drivers/media/video/cx88/cx88.h
@@ -210,6 +210,7 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55
#define CX88_BOARD_HAUPPAUGE_HVR1300 56
#define CX88_BOARD_ADSTECH_PTV_390 57
+#define CX88_BOARD_PINNACLE_PCTV_HD_800i 58
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
@@ -228,7 +229,7 @@ struct cx88_input {
enum cx88_itype type;
u32 gpio0, gpio1, gpio2, gpio3;
unsigned int vmux:2;
- unsigned int extadc:1;
+ unsigned int audioroute:2;
};
struct cx88_board {
@@ -461,6 +462,7 @@ struct cx8802_dev {
u32 mailbox;
int width;
int height;
+ unsigned char mpeg_active; /* nonzero if mpeg encoder is active */
/* mpeg params */
struct cx2341x_mpeg_params params;
@@ -588,6 +590,7 @@ extern void cx88_call_i2c_clients(struct cx88_core *core,
/* ----------------------------------------------------------- */
/* cx88-cards.c */
+extern int cx88_tuner_callback(void *dev, int command, int arg);
extern int cx88_get_resources(const struct cx88_core *core,
struct pci_dev *pci);
extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);
@@ -633,12 +636,6 @@ int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev,
void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
void cx8802_cancel_buffers(struct cx8802_dev *dev);
-int cx8802_init_common(struct cx8802_dev *dev);
-void cx8802_fini_common(struct cx8802_dev *dev);
-
-int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state);
-int cx8802_resume_common(struct pci_dev *pci_dev);
-
/* ----------------------------------------------------------- */
/* cx88-video.c*/
extern const u32 cx88_user_ctrls[];
diff --git a/trunk/drivers/media/video/em28xx/Kconfig b/trunk/drivers/media/video/em28xx/Kconfig
index c1127802ad9c..abbd38c1ebba 100644
--- a/trunk/drivers/media/video/em28xx/Kconfig
+++ b/trunk/drivers/media/video/em28xx/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_EM28XX
tristate "Empia EM2800/2820/2840 USB video capture support"
- depends on VIDEO_V4L1 && I2C && INPUT
+ depends on VIDEO_DEV && I2C && INPUT
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
@@ -11,3 +11,18 @@ config VIDEO_EM28XX
To compile this driver as a module, choose M here: the
module will be called em28xx
+
+config VIDEO_EM28XX_ALSA
+ depends on VIDEO_EM28XX
+ tristate "Empia EM28xx ALSA audio module"
+ ---help---
+ This is an ALSA driver for some Empia 28xx based TV cards.
+
+ This is not required for em2800/em2820/em2821 boards. However,
+ newer em28xx devices uses Vendor Class for audio, instead of
+ implementing the USB Audio Class. For those chips, this module
+ will enable digital audio.
+
+ To compile this driver as a module, choose M here: the
+ module will be called em28xx-alsa
+
diff --git a/trunk/drivers/media/video/em28xx/Makefile b/trunk/drivers/media/video/em28xx/Makefile
index 826d0e340753..0924550992d0 100644
--- a/trunk/drivers/media/video/em28xx/Makefile
+++ b/trunk/drivers/media/video/em28xx/Makefile
@@ -1,6 +1,12 @@
em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
em28xx-input.o
+em28xx-alsa-objs := em28xx-audio.o
+
obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
+obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+
diff --git a/trunk/drivers/media/video/em28xx/em28xx-audio.c b/trunk/drivers/media/video/em28xx/em28xx-audio.c
new file mode 100644
index 000000000000..941357c4f3f5
--- /dev/null
+++ b/trunk/drivers/media/video/em28xx/em28xx-audio.c
@@ -0,0 +1,489 @@
+/*
+ * Empiatech em28x1 audio extension
+ *
+ * Copyright (C) 2006 Markus Rechberger
+ *
+ * Copyright (C) 2007 Mauro Carvalho Chehab
+ * - Port to work with the in-kernel driver
+ * - Several cleanups
+ *
+ * This driver is based on my previous au600 usb pstn audio driver
+ * and inherits all the copyrights
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "em28xx.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_INFO "em28xx-audio %s: " fmt, \
+ __FUNCTION__, ##arg); \
+ } while (0)
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+
+static int em28xx_isoc_audio_deinit(struct em28xx *dev)
+{
+ int i;
+
+ dprintk("Stopping isoc\n");
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ usb_kill_urb(dev->adev->urb[i]);
+ usb_free_urb(dev->adev->urb[i]);
+ dev->adev->urb[i] = NULL;
+ }
+
+ return 0;
+}
+
+static void em28xx_audio_isocirq(struct urb *urb)
+{
+ struct em28xx *dev = urb->context;
+ int i;
+ unsigned int oldptr;
+ unsigned long flags;
+ int period_elapsed = 0;
+ int status;
+ unsigned char *cp;
+ unsigned int stride;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ if (dev->adev->capture_pcm_substream) {
+ substream = dev->adev->capture_pcm_substream;
+ runtime = substream->runtime;
+ stride = runtime->frame_bits >> 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int length =
+ urb->iso_frame_desc[i].actual_length / stride;
+ cp = (unsigned char *)urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+
+ if (!length)
+ continue;
+
+ spin_lock_irqsave(&dev->adev->slock, flags);
+
+ oldptr = dev->adev->hwptr_done_capture;
+ dev->adev->hwptr_done_capture += length;
+ if (dev->adev->hwptr_done_capture >=
+ runtime->buffer_size)
+ dev->adev->hwptr_done_capture -=
+ runtime->buffer_size;
+
+ dev->adev->capture_transfer_done += length;
+ if (dev->adev->capture_transfer_done >=
+ runtime->period_size) {
+ dev->adev->capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ spin_unlock_irqrestore(&dev->adev->slock, flags);
+
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt =
+ runtime->buffer_size - oldptr - 1;
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ cnt * stride);
+ memcpy(runtime->dma_area, cp + cnt,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ length * stride);
+ }
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ urb->status = 0;
+
+ if (dev->adev->shutdown)
+ return;
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
+ em28xx_errdev("resubmit of audio urb failed (error=%i)\n",
+ status);
+ }
+ return;
+}
+
+static int em28xx_init_audio_isoc(struct em28xx *dev)
+{
+ int i, errCode;
+ const int sb_size = EM28XX_NUM_AUDIO_PACKETS *
+ EM28XX_AUDIO_MAX_PACKET_SIZE;
+
+ dprintk("Starting isoc transfers\n");
+
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ struct urb *urb;
+ int j, k;
+
+ dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
+ if (!dev->adev->transfer_buffer[i])
+ return -ENOMEM;
+
+ memset(dev->adev->transfer_buffer[i], 0x80, sb_size);
+ urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ urb->dev = dev->udev;
+ urb->context = dev;
+ urb->pipe = usb_rcvisocpipe(dev->udev, 0x83);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = dev->adev->transfer_buffer[i];
+ urb->interval = 1;
+ urb->complete = em28xx_audio_isocirq;
+ urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;
+ urb->transfer_buffer_length = sb_size;
+
+ for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS;
+ j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ EM28XX_AUDIO_MAX_PACKET_SIZE;
+ }
+ dev->adev->urb[i] = urb;
+ }
+
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC);
+ if (errCode) {
+ em28xx_isoc_audio_deinit(dev);
+
+ return errCode;
+ }
+ }
+
+ return 0;
+}
+
+static int em28xx_cmd(struct em28xx *dev, int cmd, int arg)
+{
+ dprintk("%s transfer\n", (dev->adev->capture_stream == STREAM_ON)?
+ "stop" : "start");
+
+ switch (cmd) {
+ case EM28XX_CAPTURE_STREAM_EN:
+ if (dev->adev->capture_stream == STREAM_OFF && arg == 1) {
+ dev->adev->capture_stream = STREAM_ON;
+ em28xx_init_audio_isoc(dev);
+ } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) {
+ dev->adev->capture_stream = STREAM_OFF;
+ em28xx_isoc_audio_deinit(dev);
+ } else {
+ printk(KERN_ERR "An underrun very likely occurred. "
+ "Ignoring it.\n");
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+ dprintk("Alocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+
+ runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static struct snd_pcm_hardware snd_em28xx_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
+ .period_bytes_min = 64, /* 12544/2, */
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98, /* 12544, */
+};
+
+static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret = 0;
+
+ dprintk("opening device and trying to acquire exclusive lock\n");
+
+ /* Sets volume, mute, etc */
+ dev->mute = 0;
+ ret = em28xx_audio_analog_set(dev);
+ if (ret < 0)
+ goto err;
+
+ runtime->hw = snd_em28xx_hw_capture;
+ if (dev->alt == 0 && dev->adev->users == 0) {
+ int errCode;
+ dev->alt = 7;
+ errCode = usb_set_interface(dev->udev, 0, 7);
+ dprintk("changing alternate number to 7\n");
+ }
+
+ dev->adev->users++;
+
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ dev->adev->capture_pcm_substream = substream;
+ runtime->private_data = dev;
+
+ return 0;
+err:
+ printk(KERN_ERR "Error while configuring em28xx mixer\n");
+ return ret;
+}
+
+static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+ dev->adev->users--;
+
+ dprintk("closing device\n");
+
+ dev->mute = 1;
+ em28xx_audio_analog_set(dev);
+
+ if (dev->adev->users == 0 && dev->adev->shutdown == 1) {
+ dprintk("audio users: %d\n", dev->adev->users);
+ dprintk("disabling audio stream!\n");
+ dev->adev->shutdown = 0;
+ dprintk("released lock\n");
+ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
+ }
+ return 0;
+}
+
+static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ unsigned int channels, rate, format;
+ int ret;
+
+ dprintk("Setting capture parameters\n");
+
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ format = params_format(hw_params);
+ rate = params_rate(hw_params);
+ channels = params_channels(hw_params);
+
+ /* TODO: set up em28xx audio chip to deliver the correct audio format,
+ current default is 48000hz multiplexed => 96000hz mono
+ which shouldn't matter since analogue TV only supports mono */
+ return 0;
+}
+
+static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("Stop capture, if needed\n");
+
+ if (dev->adev->capture_stream == STREAM_ON)
+ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
+
+ return 0;
+}
+
+static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START)?
+ "start": "stop");
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ dev->adev->shutdown = 1;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct em28xx *dev;
+
+ snd_pcm_uframes_t hwptr_done;
+ dev = snd_pcm_substream_chip(substream);
+ hwptr_done = dev->adev->hwptr_done_capture;
+
+ return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops snd_em28xx_pcm_capture = {
+ .open = snd_em28xx_capture_open,
+ .close = snd_em28xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_em28xx_hw_capture_params,
+ .hw_free = snd_em28xx_hw_capture_free,
+ .prepare = snd_em28xx_prepare,
+ .trigger = snd_em28xx_capture_trigger,
+ .pointer = snd_em28xx_capture_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+static int em28xx_audio_init(struct em28xx *dev)
+{
+ struct em28xx_audio *adev;
+ struct snd_pcm *pcm;
+ struct snd_card *card;
+ static int devnr;
+ int ret, err;
+
+ printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "
+ "non standard usbaudio\n");
+ printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
+ "Rechberger\n");
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev) {
+ printk(KERN_ERR "em28xx-audio.c: out of memory\n");
+ return -1;
+ }
+ card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0);
+ if (card == NULL) {
+ kfree(adev);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&adev->slock);
+ ret = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
+ pcm->info_flags = 0;
+ pcm->private_data = dev;
+ strcpy(pcm->name, "Empia 28xx Capture");
+ strcpy(card->driver, "Empia Em28xx Audio");
+ strcpy(card->shortname, "Em28xx Audio");
+ strcpy(card->longname, "Empia Em28xx Audio");
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return -ENOMEM;
+ }
+ adev->sndcard = card;
+ adev->udev = dev->udev;
+ dev->adev = adev;
+
+ return 0;
+}
+
+static int em28xx_audio_fini(struct em28xx *dev)
+{
+ if (dev == NULL)
+ return 0;
+
+ if (dev->adev) {
+ snd_card_free(dev->adev->sndcard);
+ kfree(dev->adev);
+ dev->adev = NULL;
+ }
+
+ return 0;
+}
+
+static struct em28xx_ops audio_ops = {
+ .id = EM28XX_AUDIO,
+ .name = "Em28xx Audio Extension",
+ .init = em28xx_audio_init,
+ .fini = em28xx_audio_fini,
+};
+
+static int __init em28xx_alsa_register(void)
+{
+ return em28xx_register_extension(&audio_ops);
+}
+
+static void __exit em28xx_alsa_unregister(void)
+{
+ em28xx_unregister_extension(&audio_ops);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Markus Rechberger ");
+MODULE_AUTHOR("Mauro Carvalho Chehab ");
+MODULE_DESCRIPTION("Em28xx Audio driver");
+
+module_init(em28xx_alsa_register);
+module_exit(em28xx_alsa_unregister);
diff --git a/trunk/drivers/media/video/em28xx/em28xx-cards.c b/trunk/drivers/media/video/em28xx/em28xx-cards.c
index 418ea8b7f85a..2159d0160df2 100644
--- a/trunk/drivers/media/video/em28xx/em28xx-cards.c
+++ b/trunk/drivers/media/video/em28xx/em28xx-cards.c
@@ -1,5 +1,6 @@
/*
- em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
+ em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
+ video capture devices
Copyright (C) 2005 Ludovico Cavedon
Markus Rechberger
@@ -35,294 +36,735 @@
#include
#include "em28xx.h"
+#include "tuner-xc2028.h"
+
+static int tuner = -1;
+module_param(tuner, int, 0444);
+MODULE_PARM_DESC(tuner, "tuner type");
+
+static unsigned int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+
+struct em28xx_hash_table {
+ unsigned long hash;
+ unsigned int model;
+ unsigned int tuner;
+};
+
+/* Boards supported by driver */
+
+#define EM2800_BOARD_UNKNOWN 0
+#define EM2820_BOARD_UNKNOWN 1
+#define EM2820_BOARD_TERRATEC_CINERGY_250 2
+#define EM2820_BOARD_PINNACLE_USB_2 3
+#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4
+#define EM2820_BOARD_MSI_VOX_USB_2 5
+#define EM2800_BOARD_TERRATEC_CINERGY_200 6
+#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7
+#define EM2800_BOARD_KWORLD_USB2800 8
+#define EM2820_BOARD_PINNACLE_DVC_90 9
+#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10
+#define EM2880_BOARD_TERRATEC_HYBRID_XS 11
+#define EM2820_BOARD_KWORLD_PVRTV2800RF 12
+#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13
+#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14
+#define EM2800_BOARD_VGEAR_POCKETTV 15
+#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16
struct em28xx_board em28xx_boards[] = {
[EM2800_BOARD_UNKNOWN] = {
.name = "Unknown EM2800 video grabber",
.is_em2800 = 1,
.vchannels = 2,
- .norm = VIDEO_MODE_PAL,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_UNKNOWN] = {
- .name = "Unknown EM2820/2840 video grabber",
+ .name = "Unknown EM2750/28xx video grabber",
.is_em2800 = 0,
- .vchannels = 2,
- .norm = VIDEO_MODE_PAL,
- .tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
- .decoder = EM28XX_SAA7113,
- .input = {{
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = 1,
- },{
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = 1,
- }},
+ .tuner_type = TUNER_ABSENT,
},
[EM2820_BOARD_KWORLD_PVRTV2800RF] = {
.name = "Kworld PVR TV 2800 RF",
.is_em2800 = 0,
.vchannels = 2,
- .norm = VIDEO_MODE_PAL,
+ .tuner_type = TUNER_TEMIC_PAL,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_TERRATEC_CINERGY_250] = {
.name = "Terratec Cinergy 250 USB",
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_PINNACLE_USB_2] = {
.name = "Pinnacle PCTV USB 2",
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
.name = "Hauppauge WinTV USB 2",
.vchannels = 3,
- .norm = VIDEO_MODE_NTSC,
.tuner_type = TUNER_PHILIPS_FM1236_MK3,
- .tda9887_conf = TDA9887_PRESENT|TDA9887_PORT1_ACTIVE|TDA9887_PORT2_ACTIVE,
- .has_tuner = 1,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE|
+ TDA9887_PORT2_ACTIVE,
.decoder = EM28XX_TVP5150,
.has_msp34xx = 1,
/*FIXME: S-Video not tested */
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = MSP_INPUT_DEFAULT,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
.amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART),
- }},
+ } },
},
- [EM2820_BOARD_MSI_VOX_USB_2] = {
- .name = "MSI VOX USB 2.0",
- .vchannels = 3,
- .norm = VIDEO_MODE_PAL,
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tda9887_conf = TDA9887_PRESENT|TDA9887_PORT1_ACTIVE|TDA9887_PORT2_ACTIVE,
- .has_tuner = 1,
- .decoder = EM28XX_SAA7114,
- .input = {{
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
+ .name = "Hauppauge WinTV HVR 900",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = 1,
+ } },
+ },
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
+ .name = "Hauppauge WinTV HVR 950",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .mts_firmware = 1,
+ .has_12mhz_i2s = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE4,
+ .vmux = TVP5150_COMPOSITE0,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
+ .vmux = TVP5150_COMPOSITE1,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
+ .vmux = TVP5150_SVIDEO,
.amux = 1,
- }},
+ } },
+
+ /* gpio's 4, 1, 0 */
+ .analog_gpio = 0x003d2d,
+ },
+ [EM2880_BOARD_TERRATEC_HYBRID_XS] = {
+ .name = "Terratec Hybrid XS",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = 1,
+ } },
+ },
+ /* maybe there's a reason behind it why Terratec sells the Hybrid XS
+ as Prodigy XS with a different PID, let's keep it separated for now
+ maybe we'll need it lateron */
+ [EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
+ .name = "Terratec Prodigy XS",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = 1,
+ } },
+ },
+ [EM2820_BOARD_MSI_VOX_USB_2] = {
+ .name = "MSI VOX USB 2.0",
+ .vchannels = 3,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .max_range_640_480 = 1,
+
+ .decoder = EM28XX_SAA7114,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE4,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = 1,
+ } },
},
[EM2800_BOARD_TERRATEC_CINERGY_200] = {
.name = "Terratec Cinergy 200 USB",
.is_em2800 = 1,
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
.name = "Leadtek Winfast USB II",
.is_em2800 = 1,
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2800_BOARD_KWORLD_USB2800] = {
.name = "Kworld USB2800",
.is_em2800 = 1,
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_PHILIPS_ATSC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_PINNACLE_DVC_90] = {
- .name = "Pinnacle Dazzle DVC 90",
+ .name = "Pinnacle Dazzle DVC 90/DVC 100",
+ .vchannels = 3,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA7113,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = 1,
+ } },
+ },
+ [EM2800_BOARD_VGEAR_POCKETTV] = {
+ .name = "V-Gear PocketTV",
+ .is_em2800 = 1,
+ .vchannels = 3,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA7113,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = 1,
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
+ .name = "Pixelview Prolink PlayTV USB 2.0",
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
- .has_tuner = 0,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = 1,
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
/* table of devices that work with this driver */
struct usb_device_id em28xx_id_table [] = {
- { USB_DEVICE(0xeb1a, 0x2800), .driver_info = EM2800_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_MSI_VOX_USB_2 },
- { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
- { USB_DEVICE(0x2304, 0x0208), .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
- { USB_DEVICE(0x2040, 0x4200), .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
- { USB_DEVICE(0x2304, 0x0207), .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0xeb1a, 0x2750),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2800),
+ .driver_info = EM2800_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2820),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2821),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2860),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2861),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2870),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2881),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2883),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0x0ccd, 0x0036),
+ .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
+ { USB_DEVICE(0x2304, 0x0208),
+ .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
+ { USB_DEVICE(0x2040, 0x4200),
+ .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+ { USB_DEVICE(0x2040, 0x4201),
+ .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+ { USB_DEVICE(0x2304, 0x0207),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2304, 0x021a),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2040, 0x6500),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+ { USB_DEVICE(0x2040, 0x6513),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x0ccd, 0x0042),
+ .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS },
+ { USB_DEVICE(0x0ccd, 0x0047),
+ .driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS },
{ },
};
+MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+
+/* EEPROM hash table for devices with generic USB IDs */
+static struct em28xx_hash_table em28xx_eeprom_hash [] = {
+ /* P/N: SA 60002070465 Tuner: TVF7533-MF */
+ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+};
+
+/* I2C devicelist hash table for devices with generic USB IDs */
+static struct em28xx_hash_table em28xx_i2c_hash[] = {
+ {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
+ {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
+};
+/* Since em28xx_pre_card_setup() requires a proper dev->model,
+ * this won't work for boards with generic PCI IDs
+ */
void em28xx_pre_card_setup(struct em28xx *dev)
{
/* request some modules */
- switch(dev->model){
- case EM2880_BOARD_TERRATEC_PRODIGY_XS:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2880_BOARD_TERRATEC_HYBRID_XS:
- {
- em28xx_write_regs_req(dev, 0x00, 0x08, "\x7d", 1); // reset through GPIO?
- break;
+ switch (dev->model) {
+ case EM2880_BOARD_TERRATEC_PRODIGY_XS:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ em28xx_write_regs(dev, XCLK_REG, "\x27", 1);
+ em28xx_write_regs(dev, I2C_CLK_REG, "\x40", 1);
+ em28xx_write_regs(dev, 0x08, "\xff", 1);
+ em28xx_write_regs(dev, 0x04, "\x00", 1);
+ msleep(100);
+ em28xx_write_regs(dev, 0x04, "\x08", 1);
+ msleep(100);
+ em28xx_write_regs(dev, 0x08, "\xff", 1);
+ msleep(50);
+ em28xx_write_regs(dev, 0x08, "\x2d", 1);
+ msleep(50);
+ em28xx_write_regs(dev, 0x08, "\x3d", 1);
+ break;
+ }
+}
+
+static int em28xx_tuner_callback(void *ptr, int command, int arg)
+{
+ int rc = 0;
+ struct em28xx *dev = ptr;
+
+ if (dev->tuner_type != TUNER_XC2028)
+ return 0;
+
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ {
+ /* GPIO and initialization codes for analog TV and radio
+ This code should be complemented for DTV, since reset
+ codes are different.
+ */
+
+ dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
+ dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);
+
+ if (dev->analog_gpio) {
+ char gpio0 = dev->analog_gpio & 0xff;
+ char gpio1 = (dev->analog_gpio >> 8) & 0xff;
+ char gpio4 = dev->analog_gpio >> 24;
+
+ if (gpio4) {
+ dev->em28xx_write_regs(dev, 0x04, &gpio4, 1);
+ msleep(140);
}
+
+ msleep(6);
+ dev->em28xx_write_regs(dev, 0x08, &gpio0, 1);
+ msleep(10);
+ dev->em28xx_write_regs(dev, 0x08, &gpio1, 1);
+ msleep(5);
+ }
+
+ break;
+ }
+ }
+ return rc;
+}
+
+static void em28xx_config_tuner(struct em28xx *dev)
+{
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+ struct tuner_setup tun_setup;
+ struct v4l2_frequency f;
+
+ if (dev->tuner_type == TUNER_ABSENT)
+ return;
+
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+ tun_setup.tuner_callback = em28xx_tuner_callback;
+
+ em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
+
+ if (dev->tuner_type == TUNER_XC2028) {
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.fname = XC2028_DEFAULT_FIRMWARE;
+ ctl.max_len = 64;
+ ctl.mts = em28xx_boards[dev->model].mts_firmware;
+
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+
+ em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
+ }
+
+ /* configure tuner */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_ANALOG_TV;
+ f.frequency = 9076; /* just a magic number */
+ dev->ctl_freq = f.frequency;
+ em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
+}
+
+static int em28xx_hint_board(struct em28xx *dev)
+{
+ int i;
+
+ /* HINT method: EEPROM
+ *
+ * This method works only for boards with eeprom.
+ * Uses a hash of all eeprom bytes. The hash should be
+ * unique for a vendor/tuner pair.
+ * There are a high chance that tuners for different
+ * video standards produce different hashes.
+ */
+ for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) {
+ if (dev->hash == em28xx_eeprom_hash[i].hash) {
+ dev->model = em28xx_eeprom_hash[i].model;
+ dev->tuner_type = em28xx_eeprom_hash[i].tuner;
+
+ em28xx_errdev("Your board has no unique USB ID.\n");
+ em28xx_errdev("A hint were successfully done, "
+ "based on eeprom hash.\n");
+ em28xx_errdev("This method is not 100%% failproof.\n");
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+ " \n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+ return 0;
+ }
+ }
+
+ /* HINT method: I2C attached devices
+ *
+ * This method works for all boards.
+ * Uses a hash of i2c scanned devices.
+ * Devices with the same i2c attached chips will
+ * be considered equal.
+ * This method is less precise than the eeprom one.
+ */
+
+ /* user did not request i2c scanning => do it now */
+ if (!dev->i2c_hash)
+ em28xx_do_i2c_scan(dev);
+
+ for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
+ if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
+ dev->model = em28xx_i2c_hash[i].model;
+ dev->tuner_type = em28xx_i2c_hash[i].tuner;
+ em28xx_errdev("Your board has no unique USB ID.\n");
+ em28xx_errdev("A hint were successfully done, "
+ "based on i2c devicelist hash.\n");
+ em28xx_errdev("This method is not 100%% failproof.\n");
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+ " \n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+ return 0;
+ }
+ }
+
+ em28xx_errdev("Your board has no unique USB ID and thus need a "
+ "hint to be detected.\n");
+ em28xx_errdev("You may try to use card= insmod option to "
+ "workaround that.\n");
+ em28xx_errdev("Please send an email with this log to:\n");
+ em28xx_errdev("\tV4L Mailing List \n");
+ em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash);
+ em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash);
+
+ em28xx_errdev("Here is a list of valid choices for the card="
+ " insmod option:\n");
+ for (i = 0; i < em28xx_bcount; i++) {
+ em28xx_errdev(" card=%d -> %s\n",
+ i, em28xx_boards[i].name);
+ }
+ return -1;
+}
+
+
+static void em28xx_set_model(struct em28xx *dev)
+{
+ dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
+ dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx;
+ dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
+ dev->decoder = em28xx_boards[dev->model].decoder;
+ dev->video_inputs = em28xx_boards[dev->model].vchannels;
+ dev->analog_gpio = em28xx_boards[dev->model].analog_gpio;
+ dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s;
+ dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480;
+}
+
+/* ----------------------------------------------------------------------- */
+void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir)
+{
+ if (disable_ir) {
+ ir->get_key = NULL;
+ return ;
+ }
+
+ /* detect & configure */
+ switch (dev->model) {
+ case (EM2800_BOARD_UNKNOWN):
+ break;
+ case (EM2820_BOARD_UNKNOWN):
+ break;
+ case (EM2800_BOARD_TERRATEC_CINERGY_200):
+ case (EM2820_BOARD_TERRATEC_CINERGY_250):
+ ir->ir_codes = ir_codes_em_terratec;
+ ir->get_key = em28xx_get_key_terratec;
+ snprintf(ir->c.name, sizeof(ir->c.name),
+ "i2c IR (EM28XX Terratec)");
+ break;
+ case (EM2820_BOARD_PINNACLE_USB_2):
+ ir->ir_codes = ir_codes_pinnacle_grey;
+ ir->get_key = em28xx_get_key_pinnacle_usb_grey;
+ snprintf(ir->c.name, sizeof(ir->c.name),
+ "i2c IR (EM28XX Pinnacle PCTV)");
+ break;
+ case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2):
+ ir->ir_codes = ir_codes_hauppauge_new;
+ ir->get_key = em28xx_get_key_em_haup;
+ snprintf(ir->c.name, sizeof(ir->c.name),
+ "i2c IR (EM2840 Hauppauge)");
+ break;
+ case (EM2820_BOARD_MSI_VOX_USB_2):
+ break;
+ case (EM2800_BOARD_LEADTEK_WINFAST_USBII):
+ break;
+ case (EM2800_BOARD_KWORLD_USB2800):
+ break;
}
}
void em28xx_card_setup(struct em28xx *dev)
{
+ em28xx_set_model(dev);
+
+ dev->tuner_type = em28xx_boards[dev->model].tuner_type;
+
/* request some modules */
- switch(dev->model){
- case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
- {
- struct tveeprom tv;
+ switch (dev->model) {
+ case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ {
+ struct tveeprom tv;
#ifdef CONFIG_MODULES
- request_module("tveeprom");
- request_module("ir-kbd-i2c");
- request_module("msp3400");
+ request_module("tveeprom");
#endif
- /* Call first TVeeprom */
-
- dev->i2c_client.addr = 0xa0 >> 1;
- tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
-
- dev->tuner_type= tv.tuner_type;
- if (tv.audio_processor == AUDIO_CHIP_MSP34XX) {
- dev->i2s_speed=2048000;
- dev->has_msp34xx=1;
- } else
- dev->has_msp34xx=0;
- break;
- }
- case EM2820_BOARD_KWORLD_PVRTV2800RF:
- {
- em28xx_write_regs_req(dev,0x00,0x08, "\xf9", 1); // GPIO enables sound on KWORLD PVR TV 2800RF
- break;
- }
+ /* Call first TVeeprom */
+
+ dev->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
+ dev->tuner_type = tv.tuner_type;
+
+ if (tv.audio_processor == AUDIO_CHIP_MSP34XX) {
+ dev->i2s_speed = 2048000;
+ dev->has_msp34xx = 1;
+ }
+#ifdef CONFIG_MODULES
+ if (tv.has_ir)
+ request_module("ir-kbd-i2c");
+#endif
+ break;
+ }
+ case EM2820_BOARD_KWORLD_PVRTV2800RF:
+ /* GPIO enables sound on KWORLD PVR TV 2800RF */
+ em28xx_write_regs_req(dev, 0x00, 0x08, "\xf9", 1);
+ break;
+ case EM2820_BOARD_UNKNOWN:
+ case EM2800_BOARD_UNKNOWN:
+ if (!em28xx_hint_board(dev))
+ em28xx_set_model(dev);
}
-}
-MODULE_DEVICE_TABLE (usb, em28xx_id_table);
+ /* Allow override tuner type by a module parameter */
+ if (tuner >= 0)
+ dev->tuner_type = tuner;
+
+#ifdef CONFIG_MODULES
+ /* request some modules */
+ if (dev->has_msp34xx)
+ request_module("msp3400");
+ if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
+ request_module("saa7115");
+ if (dev->decoder == EM28XX_TVP5150)
+ request_module("tvp5150");
+ if (dev->tuner_type != TUNER_ABSENT)
+ request_module("tuner");
+#endif
+
+ em28xx_config_tuner(dev);
+}
diff --git a/trunk/drivers/media/video/em28xx/em28xx-core.c b/trunk/drivers/media/video/em28xx/em28xx-core.c
index d56484f20467..f6b78357f0e5 100644
--- a/trunk/drivers/media/video/em28xx/em28xx-core.c
+++ b/trunk/drivers/media/video/em28xx/em28xx-core.c
@@ -252,7 +252,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
* em28xx_write_ac97()
* write a 16 bit value to the specified AC97 address (LSB first!)
*/
-int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val)
+static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val)
{
int ret;
u8 addr = reg & 0x7f;
@@ -268,16 +268,98 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val)
return 0;
}
+int em28xx_set_audio_source(struct em28xx *dev)
+{
+ static char *enable = "\x08\x08";
+ static char *disable = "\x08\x88";
+ char *video = enable, *line = disable;
+ int ret, no_ac97;
+ u8 input;
+
+ if (dev->is_em2800) {
+ if (dev->ctl_ainput)
+ input = EM2800_AUDIO_SRC_LINE;
+ else
+ input = EM2800_AUDIO_SRC_TUNER;
+
+ ret = em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &input, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (dev->has_msp34xx)
+ input = EM28XX_AUDIO_SRC_TUNER;
+ else {
+ switch (dev->ctl_ainput) {
+ case EM28XX_AMUX_VIDEO:
+ input = EM28XX_AUDIO_SRC_TUNER;
+ no_ac97 = 1;
+ break;
+ case EM28XX_AMUX_LINE_IN:
+ input = EM28XX_AUDIO_SRC_LINE;
+ no_ac97 = 1;
+ break;
+ case EM28XX_AMUX_AC97_VIDEO:
+ input = EM28XX_AUDIO_SRC_LINE;
+ break;
+ case EM28XX_AMUX_AC97_LINE_IN:
+ input = EM28XX_AUDIO_SRC_LINE;
+ video = disable;
+ line = enable;
+ break;
+ }
+ }
+
+ ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0);
+ if (ret < 0)
+ return ret;
+
+ if (no_ac97)
+ return 0;
+
+ /* Sets AC97 mixer registers */
+
+ ret = em28xx_write_ac97(dev, VIDEO_AC97, video);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_ac97(dev, LINE_IN_AC97, line);
+
+ return ret;
+}
+
int em28xx_audio_analog_set(struct em28xx *dev)
{
+ int ret;
char s[2] = { 0x00, 0x00 };
+ u8 xclk = 0x07;
+
s[0] |= 0x1f - dev->volume;
s[1] |= 0x1f - dev->volume;
+
if (dev->mute)
s[1] |= 0x80;
- return em28xx_write_ac97(dev, MASTER_AC97, s);
-}
+ ret = em28xx_write_ac97(dev, MASTER_AC97, s);
+ if (ret < 0)
+ return ret;
+
+ if (dev->has_12mhz_i2s)
+ xclk |= 0x20;
+
+ if (!dev->mute)
+ xclk |= 0x80;
+ ret = em28xx_write_reg_bits(dev, XCLK_REG, xclk, 0xa7);
+ if (ret < 0)
+ return ret;
+ msleep(10);
+
+ /* Selects the proper audio input */
+ ret = em28xx_set_audio_source(dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
int em28xx_colorlevels_set_default(struct em28xx *dev)
{
diff --git a/trunk/drivers/media/video/em28xx/em28xx-i2c.c b/trunk/drivers/media/video/em28xx/em28xx-i2c.c
index e3a4aa7a9df4..cacd04d46e99 100644
--- a/trunk/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/trunk/drivers/media/video/em28xx/em28xx-i2c.c
@@ -25,9 +25,9 @@
#include
#include
#include
-#include
#include "em28xx.h"
+#include "tuner-xc2028.h"
#include
#include
@@ -291,6 +291,31 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
return rc;
}
+/* based on linux/sunrpc/svcauth.h and linux/hash.h
+ * The original hash function returns a different value, if arch is x86_64
+ * or i386.
+ */
+static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits)
+{
+ unsigned long hash = 0;
+ unsigned long l = 0;
+ int len = 0;
+ unsigned char c;
+ do {
+ if (len == length) {
+ c = (char)len;
+ len = -1;
+ } else
+ c = *buf++;
+ l = (l << 8) | c;
+ len++;
+ if ((len & (32 / 8 - 1)) == 0)
+ hash = ((hash^l) * 0x9e370001UL);
+ } while (len);
+
+ return (hash >> (32 - bits)) & 0xffffffffUL;
+}
+
static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
{
unsigned char buf, *p = eedata;
@@ -334,7 +359,11 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
printk("\n");
}
- printk(KERN_INFO "EEPROM ID= 0x%08x\n", em_eeprom->id);
+ if (em_eeprom->id == 0x9567eb1a)
+ dev->hash = em28xx_hash_mem(eedata, len, 32);
+
+ printk(KERN_INFO "EEPROM ID= 0x%08x, hash = 0x%08lx\n",
+ em_eeprom->id, dev->hash);
printk(KERN_INFO "Vendor/Product ID= %04x:%04x\n", em_eeprom->vendor_ID,
em_eeprom->product_ID);
@@ -391,21 +420,6 @@ static u32 functionality(struct i2c_adapter *adap)
}
-static int em28xx_set_tuner(int check_eeprom, struct i2c_client *client)
-{
- struct em28xx *dev = client->adapter->algo_data;
- struct tuner_setup tun_setup;
-
- if (dev->has_tuner) {
- tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
- tun_setup.type = dev->tuner_type;
- tun_setup.addr = dev->tuner_addr;
- em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
- }
-
- return (0);
-}
-
/*
* attach_inform()
* gets called when a device attaches to the i2c bus
@@ -421,6 +435,8 @@ static int attach_inform(struct i2c_client *client)
case 0x96:
case 0x94:
{
+ struct v4l2_priv_tun_config tda9887_cfg;
+
struct tuner_setup tun_setup;
tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
@@ -428,7 +444,11 @@ static int attach_inform(struct i2c_client *client)
tun_setup.addr = client->addr;
em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
- em28xx_i2c_call_clients(dev, TDA9887_SET_CONFIG, &dev->tda9887_conf);
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+ em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG,
+ &tda9887_cfg);
break;
}
case 0x42:
@@ -458,9 +478,11 @@ static int attach_inform(struct i2c_client *client)
break;
default:
+ if (!dev->tuner_addr)
+ dev->tuner_addr = client->addr;
+
dprintk1(1,"attach inform: detected I2C address %x\n", client->addr << 1);
- dev->tuner_addr = client->addr;
- em28xx_set_tuner(-1, client);
+
}
return 0;
@@ -510,19 +532,26 @@ static char *i2c_devs[128] = {
* do_i2c_scan()
* check i2c address range for devices
*/
-static void do_i2c_scan(char *name, struct i2c_client *c)
+void em28xx_do_i2c_scan(struct em28xx *dev)
{
+ u8 i2c_devicelist[128];
unsigned char buf;
int i, rc;
+ memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist));
+
for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
- c->addr = i;
- rc = i2c_master_recv(c, &buf, 0);
+ dev->i2c_client.addr = i;
+ rc = i2c_master_recv(&dev->i2c_client, &buf, 0);
if (rc < 0)
continue;
- printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n", name,
- i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ i2c_devicelist[i] = i;
+ printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n",
+ dev->name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
}
+
+ dev->i2c_hash = em28xx_hash_mem(i2c_devicelist,
+ ARRAY_SIZE(i2c_devicelist), 32);
}
/*
@@ -555,7 +584,7 @@ int em28xx_i2c_register(struct em28xx *dev)
em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
if (i2c_scan)
- do_i2c_scan(dev->name, &dev->i2c_client);
+ em28xx_do_i2c_scan(dev);
return 0;
}
diff --git a/trunk/drivers/media/video/em28xx/em28xx-input.c b/trunk/drivers/media/video/em28xx/em28xx-input.c
index e3894b68c4ee..10da2fd8d987 100644
--- a/trunk/drivers/media/video/em28xx/em28xx-input.c
+++ b/trunk/drivers/media/video/em28xx/em28xx-input.c
@@ -30,11 +30,7 @@
#include "em28xx.h"
-static unsigned int disable_ir = 0;
-module_param(disable_ir, int, 0444);
-MODULE_PARM_DESC(disable_ir,"disable infrared remote support");
-
-static unsigned int ir_debug = 0;
+static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
@@ -43,7 +39,7 @@ MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
/* ----------------------------------------------------------------------- */
-static int get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char b;
@@ -72,7 +68,7 @@ static int get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
}
-static int get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char buf[2];
unsigned char code;
@@ -103,7 +99,8 @@ static int get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
-static int get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
{
unsigned char buf[3];
@@ -125,45 +122,6 @@ static int get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw
return 1;
}
-/* ----------------------------------------------------------------------- */
-void em28xx_set_ir(struct em28xx * dev,struct IR_i2c *ir)
-{
- if (disable_ir) {
- ir->get_key=NULL;
- return ;
- }
-
- /* detect & configure */
- switch (dev->model) {
- case (EM2800_BOARD_UNKNOWN):
- break;
- case (EM2820_BOARD_UNKNOWN):
- break;
- case (EM2800_BOARD_TERRATEC_CINERGY_200):
- case (EM2820_BOARD_TERRATEC_CINERGY_250):
- ir->ir_codes = ir_codes_em_terratec;
- ir->get_key = get_key_terratec;
- snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (EM28XX Terratec)");
- break;
- case (EM2820_BOARD_PINNACLE_USB_2):
- ir->ir_codes = ir_codes_pinnacle_grey;
- ir->get_key = get_key_pinnacle_usb_grey;
- snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (EM28XX Pinnacle PCTV)");
- break;
- case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2):
- ir->ir_codes = ir_codes_hauppauge_new;
- ir->get_key = get_key_em_haup;
- snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (EM2840 Hauppauge)");
- break;
- case (EM2820_BOARD_MSI_VOX_USB_2):
- break;
- case (EM2800_BOARD_LEADTEK_WINFAST_USBII):
- break;
- case (EM2800_BOARD_KWORLD_USB2800):
- break;
- }
-}
-
/* ----------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
diff --git a/trunk/drivers/media/video/em28xx/em28xx-video.c b/trunk/drivers/media/video/em28xx/em28xx-video.c
index 0906bc5766cc..a0c334672488 100644
--- a/trunk/drivers/media/video/em28xx/em28xx-video.c
+++ b/trunk/drivers/media/video/em28xx/em28xx-video.c
@@ -33,13 +33,12 @@
#include
#include
#include
-#include
#include
#include "em28xx.h"
-#include
#include
#include
+#include
#define DRIVER_AUTHOR "Ludovico Cavedon , " \
"Markus Rechberger , " \
@@ -48,7 +47,7 @@
#define DRIVER_NAME "em28xx"
#define DRIVER_DESC "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 0, 1)
+#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0)
#define em28xx_videodbg(fmt, arg...) do {\
if (video_debug) \
@@ -63,17 +62,17 @@ static LIST_HEAD(em28xx_devlist);
static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+
module_param_array(card, int, NULL, 0444);
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
-MODULE_PARM_DESC(card,"card type");
-MODULE_PARM_DESC(video_nr,"video device numbers");
-MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
-
-static int tuner = -1;
-module_param(tuner, int, 0444);
-MODULE_PARM_DESC(tuner, "tuner type");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
static unsigned int video_debug = 0;
module_param(video_debug,int,0644);
@@ -82,29 +81,6 @@ MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
static unsigned long em28xx_devused;
-/* supported tv norms */
-static struct em28xx_tvnorm tvnorms[] = {
- {
- .name = "PAL",
- .id = V4L2_STD_PAL,
- .mode = VIDEO_MODE_PAL,
- }, {
- .name = "NTSC",
- .id = V4L2_STD_NTSC,
- .mode = VIDEO_MODE_NTSC,
- }, {
- .name = "SECAM",
- .id = V4L2_STD_SECAM,
- .mode = VIDEO_MODE_SECAM,
- }, {
- .name = "PAL-M",
- .id = V4L2_STD_PAL_M,
- .mode = VIDEO_MODE_PAL,
- }
-};
-
-#define TVNORMS ARRAY_SIZE(tvnorms)
-
/* supported controls */
/* Common to all boards */
static struct v4l2_queryctrl em28xx_qctrl[] = {
@@ -131,8 +107,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
static struct usb_driver em28xx_usb_driver;
-static DEFINE_MUTEX(em28xx_sysfs_lock);
-static DECLARE_RWSEM(em28xx_disconnect);
/********************* v4l2 interface ******************************************/
@@ -153,11 +127,9 @@ static int em28xx_config(struct em28xx *dev)
/* em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */
em28xx_write_regs_req(dev,0x00,0x11,"\x51",1);
- em28xx_audio_usb_mute(dev, 1);
dev->mute = 1; /* maybe not the right place... */
dev->volume = 0x1f;
- em28xx_audio_analog_set(dev);
- em28xx_audio_analog_setup(dev);
+
em28xx_outfmt_set_yuv422(dev);
em28xx_colorlevels_set_default(dev);
em28xx_compression_disable(dev);
@@ -171,7 +143,6 @@ static int em28xx_config(struct em28xx *dev)
*/
static void em28xx_config_i2c(struct em28xx *dev)
{
- struct v4l2_frequency f;
struct v4l2_routing route;
route.input = INPUT(dev->ctl_input)->vmux;
@@ -179,18 +150,6 @@ static void em28xx_config_i2c(struct em28xx *dev)
em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL);
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
-
- /* configure tuner */
- f.tuner = 0;
- f.type = V4L2_TUNER_ANALOG_TV;
- f.frequency = 9076; /* FIXME:remove magic number */
- dev->ctl_freq = f.frequency;
- em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
-
- /* configure tda9887 */
-
-
-/* em28xx_i2c_call_clients(dev,VIDIOC_S_STD,&dev->tvnorm->id); */
}
/*
@@ -212,7 +171,6 @@ static void em28xx_empty_framequeues(struct em28xx *dev)
static void video_mux(struct em28xx *dev, int index)
{
- int ainput;
struct v4l2_routing route;
route.input = INPUT(index)->vmux;
@@ -222,8 +180,6 @@ static void video_mux(struct em28xx *dev, int index)
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
- em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,route.input,dev->ctl_ainput);
-
if (dev->has_msp34xx) {
if (dev->i2s_speed)
em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed);
@@ -231,1314 +187,1693 @@ static void video_mux(struct em28xx *dev, int index)
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
/* Note: this is msp3400 specific */
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
- ainput = EM28XX_AUDIO_SRC_TUNER;
- em28xx_audio_source(dev, ainput);
- } else {
- switch (dev->ctl_ainput) {
- case 0:
- ainput = EM28XX_AUDIO_SRC_TUNER;
- break;
- default:
- ainput = EM28XX_AUDIO_SRC_LINE;
- }
- em28xx_audio_source(dev, ainput);
}
+
+ em28xx_set_audio_source(dev);
}
-/*
- * em28xx_v4l2_open()
- * inits the device and starts isoc transfer
- */
-static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
+/* Usage lock check functions */
+static int res_get(struct em28xx_fh *fh)
{
- int minor = iminor(inode);
- int errCode = 0;
- struct em28xx *h,*dev = NULL;
-
- list_for_each_entry(h, &em28xx_devlist, devlist) {
- if (h->vdev->minor == minor) {
- dev = h;
- dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- if (h->vbi_dev->minor == minor) {
- dev = h;
- dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
- }
- if (NULL == dev)
- return -ENODEV;
-
- em28xx_videodbg("open minor=%d type=%s users=%d\n",
- minor,v4l2_type_names[dev->type],dev->users);
-
- if (!down_read_trylock(&em28xx_disconnect))
- return -ERESTARTSYS;
-
- if (dev->users) {
- em28xx_warn("this driver can be opened only once\n");
- up_read(&em28xx_disconnect);
- return -EBUSY;
- }
+ struct em28xx *dev = fh->dev;
+ int rc = 0;
- mutex_init(&dev->fileop_lock); /* to 1 == available */
- spin_lock_init(&dev->queue_lock);
- init_waitqueue_head(&dev->wait_frame);
- init_waitqueue_head(&dev->wait_stream);
+ /* This instance already has stream_on */
+ if (fh->stream_on)
+ return rc;
mutex_lock(&dev->lock);
- if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- em28xx_set_alternate(dev);
-
- dev->width = norm_maxw(dev);
- dev->height = norm_maxh(dev);
- dev->frame_size = dev->width * dev->height * 2;
- dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
- dev->bytesperline = dev->width * 2;
- dev->hscale = 0;
- dev->vscale = 0;
-
- em28xx_capture_start(dev, 1);
- em28xx_resolution_set(dev);
-
- /* device needs to be initialized before isoc transfer */
- video_mux(dev, 0);
-
- /* start the transfer */
- errCode = em28xx_init_isoc(dev);
- if (errCode)
- goto err;
-
+ if (dev->stream_on)
+ rc = -EINVAL;
+ else {
+ dev->stream_on = 1;
+ fh->stream_on = 1;
}
- dev->users++;
- filp->private_data = dev;
- dev->io = IO_NONE;
- dev->stream = STREAM_OFF;
- dev->num_frames = 0;
+ mutex_unlock(&dev->lock);
+ return rc;
+}
- /* prepare queues */
- em28xx_empty_framequeues(dev);
+static int res_check(struct em28xx_fh *fh)
+{
+ return (fh->stream_on);
+}
- dev->state |= DEV_INITIALIZED;
+static void res_free(struct em28xx_fh *fh)
+{
+ struct em28xx *dev = fh->dev;
-err:
+ mutex_lock(&dev->lock);
+ fh->stream_on = 0;
+ dev->stream_on = 0;
mutex_unlock(&dev->lock);
- up_read(&em28xx_disconnect);
- return errCode;
}
/*
- * em28xx_realease_resources()
- * unregisters the v4l2,i2c and usb devices
- * called when the device gets disconected or at module unload
-*/
-static void em28xx_release_resources(struct em28xx *dev)
+ * em28xx_vm_open()
+ */
+static void em28xx_vm_open(struct vm_area_struct *vma)
{
- mutex_lock(&em28xx_sysfs_lock);
-
- /*FIXME: I2C IR should be disconnected */
-
- em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
- dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
- dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
- list_del(&dev->devlist);
- video_unregister_device(dev->vdev);
- video_unregister_device(dev->vbi_dev);
- em28xx_i2c_unregister(dev);
- usb_put_dev(dev->udev);
- mutex_unlock(&em28xx_sysfs_lock);
-
-
- /* Mark device as unused */
- em28xx_devused&=~(1<devno);
+ struct em28xx_frame_t *f = vma->vm_private_data;
+ f->vma_use_count++;
}
/*
- * em28xx_v4l2_close()
- * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
+ * em28xx_vm_close()
*/
-static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
+static void em28xx_vm_close(struct vm_area_struct *vma)
{
- int errCode;
- struct em28xx *dev=filp->private_data;
-
- em28xx_videodbg("users=%d\n", dev->users);
+ /* NOTE: buffers are not freed here */
+ struct em28xx_frame_t *f = vma->vm_private_data;
- mutex_lock(&dev->lock);
+ if (f->vma_use_count)
+ f->vma_use_count--;
+}
- em28xx_uninit_isoc(dev);
+static struct vm_operations_struct em28xx_vm_ops = {
+ .open = em28xx_vm_open,
+ .close = em28xx_vm_close,
+};
- em28xx_release_buffers(dev);
- /* the device is already disconnect, free the remaining resources */
- if (dev->state & DEV_DISCONNECTED) {
- em28xx_release_resources(dev);
- mutex_unlock(&dev->lock);
- kfree(dev);
+/*
+ * em28xx_get_ctrl()
+ * return the current saturation, brightness or contrast, mute state
+ */
+static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = dev->mute;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = dev->volume;
return 0;
+ default:
+ return -EINVAL;
}
+}
- /* set alternate 0 */
- dev->alt = 0;
- em28xx_videodbg("setting alternate 0\n");
- errCode = usb_set_interface(dev->udev, 0, 0);
- if (errCode < 0) {
- em28xx_errdev ("cannot change alternate number to 0 (error=%i)\n",
- errCode);
+/*
+ * em28xx_set_ctrl()
+ * mute or set new saturation, brightness or contrast
+ */
+static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value != dev->mute) {
+ dev->mute = ctrl->value;
+ return em28xx_audio_analog_set(dev);
+ }
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->volume = ctrl->value;
+ return em28xx_audio_analog_set(dev);
+ default:
+ return -EINVAL;
}
-
- dev->users--;
- wake_up_interruptible_nr(&dev->open, 1);
- mutex_unlock(&dev->lock);
- return 0;
}
/*
- * em28xx_v4l2_read()
- * will allocate buffers when called for the first time
+ * em28xx_stream_interrupt()
+ * stops streaming
*/
-static ssize_t
-em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
- loff_t * f_pos)
+static int em28xx_stream_interrupt(struct em28xx *dev)
{
- struct em28xx_frame_t *f, *i;
- unsigned long lock_flags;
- int ret = 0;
- struct em28xx *dev = filp->private_data;
+ int rc = 0;
- if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
- }
- if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
- em28xx_videodbg("not supported yet! ...\n");
- if (copy_to_user(buf, "", 1)) {
- mutex_unlock(&dev->fileop_lock);
- return -EFAULT;
- }
- return (1);
- }
- if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n");
- em28xx_videodbg("not supported yet! ...\n");
- if (copy_to_user(buf, "", 1)) {
- mutex_unlock(&dev->fileop_lock);
- return -EFAULT;
- }
- return (1);
+ /* stop reading from the device */
+
+ dev->stream = STREAM_INTERRUPT;
+ rc = wait_event_timeout(dev->wait_stream,
+ (dev->stream == STREAM_OFF) ||
+ (dev->state & DEV_DISCONNECTED),
+ EM28XX_URB_TIMEOUT);
+
+ if (rc) {
+ dev->state |= DEV_MISCONFIGURED;
+ em28xx_videodbg("device is misconfigured; close and "
+ "open /dev/video%d again\n",
+ dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
+ return rc;
}
- if (mutex_lock_interruptible(&dev->fileop_lock))
- return -ERESTARTSYS;
+ return 0;
+}
+
+static int check_dev(struct em28xx *dev)
+{
if (dev->state & DEV_DISCONNECTED) {
- em28xx_videodbg("device not present\n");
- mutex_unlock(&dev->fileop_lock);
+ em28xx_errdev("v4l2 ioctl: device not present\n");
return -ENODEV;
}
if (dev->state & DEV_MISCONFIGURED) {
- em28xx_videodbg("device misconfigured; close and open it again\n");
- mutex_unlock(&dev->fileop_lock);
+ em28xx_errdev("v4l2 ioctl: device is misconfigured; "
+ "close and open it again\n");
return -EIO;
}
+ return 0;
+}
- if (dev->io == IO_MMAP) {
- em28xx_videodbg ("IO method is set to mmap; close and open"
- " the device again to choose the read method\n");
- mutex_unlock(&dev->fileop_lock);
- return -EINVAL;
- }
+static void get_scale(struct em28xx *dev,
+ unsigned int width, unsigned int height,
+ unsigned int *hscale, unsigned int *vscale)
+{
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
- if (dev->io == IO_NONE) {
- if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
- em28xx_errdev("read failed, not enough memory\n");
- mutex_unlock(&dev->fileop_lock);
- return -ENOMEM;
- }
- dev->io = IO_READ;
- dev->stream = STREAM_ON;
- em28xx_queue_unusedframes(dev);
- }
+ *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
+ if (*hscale >= 0x4000)
+ *hscale = 0x3fff;
- if (!count) {
- mutex_unlock(&dev->fileop_lock);
- return 0;
- }
+ *vscale = (((unsigned long)maxh) << 12) / height - 4096L;
+ if (*vscale >= 0x4000)
+ *vscale = 0x3fff;
+}
- if (list_empty(&dev->outqueue)) {
- if (filp->f_flags & O_NONBLOCK) {
- mutex_unlock(&dev->fileop_lock);
- return -EAGAIN;
- }
- ret = wait_event_interruptible
- (dev->wait_frame,
- (!list_empty(&dev->outqueue)) ||
- (dev->state & DEV_DISCONNECTED));
- if (ret) {
- mutex_unlock(&dev->fileop_lock);
- return ret;
- }
- if (dev->state & DEV_DISCONNECTED) {
- mutex_unlock(&dev->fileop_lock);
- return -ENODEV;
- }
- }
+/* ------------------------------------------------------------------
+ IOCTL vidioc handling
+ ------------------------------------------------------------------*/
- f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame);
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
- spin_lock_irqsave(&dev->queue_lock, lock_flags);
- list_for_each_entry(i, &dev->outqueue, frame)
- i->state = F_UNUSED;
- INIT_LIST_HEAD(&dev->outqueue);
- spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+ mutex_lock(&dev->lock);
- em28xx_queue_unusedframes(dev);
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ f->fmt.pix.bytesperline = dev->bytesperline;
+ f->fmt.pix.sizeimage = dev->frame_size;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- if (count > f->buf.length)
- count = f->buf.length;
+ /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
+ f->fmt.pix.field = dev->interlaced ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
- if (copy_to_user(buf, f->bufmem, count)) {
- mutex_unlock(&dev->fileop_lock);
- return -EFAULT;
- }
- *f_pos += count;
+ mutex_unlock(&dev->lock);
+ return 0;
+}
- mutex_unlock(&dev->fileop_lock);
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int width = f->fmt.pix.width;
+ int height = f->fmt.pix.height;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+ unsigned int hscale, vscale;
+
+ /* width must even because of the YUYV format
+ height must be even because of interlacing */
+ height &= 0xfffe;
+ width &= 0xfffe;
- return count;
+ if (height < 32)
+ height = 32;
+ if (height > maxh)
+ height = maxh;
+ if (width < 48)
+ width = 48;
+ if (width > maxw)
+ width = maxw;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->is_em2800) {
+ /* the em2800 can only scale down to 50% */
+ if (height % (maxh / 2))
+ height = maxh;
+ if (width % (maxw / 2))
+ width = maxw;
+ /* according to empiatech support */
+ /* the MaxPacketSize is to small to support */
+ /* framesizes larger than 640x480 @ 30 fps */
+ /* or 640x576 @ 25 fps. As this would cut */
+ /* of a part of the image we prefer */
+ /* 360x576 or 360x480 for now */
+ if (width == maxw && height == maxh)
+ width /= 2;
+ }
+
+ get_scale(dev, width, height, &hscale, &vscale);
+
+ width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+ height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
+
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ f->fmt.pix.bytesperline = width * 2;
+ f->fmt.pix.sizeimage = width * 2 * height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ mutex_unlock(&dev->lock);
+ return 0;
}
-/*
- * em28xx_v4l2_poll()
- * will allocate buffers when called for the first time
- */
-static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- unsigned int mask = 0;
- struct em28xx *dev = filp->private_data;
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc, i;
- if (mutex_lock_interruptible(&dev->fileop_lock))
- return POLLERR;
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
- if (dev->state & DEV_DISCONNECTED) {
- em28xx_videodbg("device not present\n");
- } else if (dev->state & DEV_MISCONFIGURED) {
- em28xx_videodbg("device is misconfigured; close and open it again\n");
+ vidioc_try_fmt_cap(file, priv, f);
+
+ mutex_lock(&dev->lock);
+
+ for (i = 0; i < dev->num_frames; i++)
+ if (dev->frame[i].vma_use_count) {
+ em28xx_videodbg("VIDIOC_S_FMT failed. "
+ "Unmap the buffers first.\n");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* stop io in case it is already in progress */
+ if (dev->stream == STREAM_ON) {
+ em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
+ rc = em28xx_stream_interrupt(dev);
+ if (rc < 0)
+ goto err;
+ }
+
+ em28xx_release_buffers(dev);
+ dev->io = IO_NONE;
+
+ /* set new image size */
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+ dev->frame_size = dev->width * dev->height * 2;
+ dev->field_size = dev->frame_size >> 1;
+ dev->bytesperline = dev->width * 2;
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ /* FIXME: This is really weird! Why capture is starting with
+ this ioctl ???
+ */
+ em28xx_uninit_isoc(dev);
+ em28xx_set_alternate(dev);
+ em28xx_capture_start(dev, 1);
+ em28xx_resolution_set(dev);
+ em28xx_init_isoc(dev);
+ rc = 0;
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ struct v4l2_format f;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&dev->lock);
+ dev->norm = *norm;
+ mutex_unlock(&dev->lock);
+
+ /* Adjusts width/height, if needed */
+ f.fmt.pix.width = dev->width;
+ f.fmt.pix.height = dev->height;
+ vidioc_try_fmt_cap(file, priv, &f);
+
+ mutex_lock(&dev->lock);
+
+ /* set new image size */
+ dev->width = f.fmt.pix.width;
+ dev->height = f.fmt.pix.height;
+ dev->frame_size = dev->width * dev->height * 2;
+ dev->field_size = dev->frame_size >> 1;
+ dev->bytesperline = dev->width * 2;
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ em28xx_resolution_set(dev);
+ em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->norm);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static const char *iname[] = {
+ [EM28XX_VMUX_COMPOSITE1] = "Composite1",
+ [EM28XX_VMUX_COMPOSITE2] = "Composite2",
+ [EM28XX_VMUX_COMPOSITE3] = "Composite3",
+ [EM28XX_VMUX_COMPOSITE4] = "Composite4",
+ [EM28XX_VMUX_SVIDEO] = "S-Video",
+ [EM28XX_VMUX_TELEVISION] = "Television",
+ [EM28XX_VMUX_CABLE] = "Cable TV",
+ [EM28XX_VMUX_DVB] = "DVB",
+ [EM28XX_VMUX_DEBUG] = "for debug only",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
+ (EM28XX_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ i->std = dev->vdev->tvnorms;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ *i = dev->ctl_input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (i >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(i)->type)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ video_mux(dev, i);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned int index = a->index;
+
+ if (a->index > 1)
+ return -EINVAL;
+
+ index = dev->ctl_ainput;
+
+ if (index == 0) {
+ strcpy(a->name, "Television");
} else {
- if (dev->io == IO_NONE) {
- if (!em28xx_request_buffers
- (dev, EM28XX_NUM_READ_FRAMES)) {
- em28xx_warn
- ("poll() failed, not enough memory\n");
- } else {
- dev->io = IO_READ;
- dev->stream = STREAM_ON;
+ strcpy(a->name, "Line In");
+ }
+ a->capability = V4L2_AUDCAP_STEREO;
+ a->index = index;
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (a->index != dev->ctl_ainput)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int id = qc->id;
+ int i;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ memset(qc, 0, sizeof(*qc));
+
+ qc->id = id;
+
+ if (!dev->has_msp34xx) {
+ for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+ if (qc->id && qc->id == em28xx_qctrl[i].id) {
+ memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+ return 0;
}
}
+ }
+ mutex_lock(&dev->lock);
+ em28xx_i2c_call_clients(dev, VIDIOC_QUERYCTRL, qc);
+ mutex_unlock(&dev->lock);
- if (dev->io == IO_READ) {
- em28xx_queue_unusedframes(dev);
- poll_wait(filp, &dev->wait_frame, wait);
+ if (qc->type)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+ mutex_lock(&dev->lock);
+
+ if (!dev->has_msp34xx)
+ rc = em28xx_get_ctrl(dev, ctrl);
+ else
+ rc = -EINVAL;
+
+ if (rc == -EINVAL) {
+ em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
+ rc = 0;
+ }
+
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ u8 i;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->has_msp34xx)
+ em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
+ else {
+ rc = 1;
+ for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+ if (ctrl->id == em28xx_qctrl[i].id) {
+ if (ctrl->value < em28xx_qctrl[i].minimum ||
+ ctrl->value > em28xx_qctrl[i].maximum) {
+ rc = -ERANGE;
+ break;
+ }
+
+ rc = em28xx_set_ctrl(dev, ctrl);
+ break;
+ }
+ }
+ }
+
+ /* Control not found - try to send it to the attached devices */
+ if (rc == 1) {
+ em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
+ rc = 0;
+ }
+
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Tuner");
+
+ mutex_lock(&dev->lock);
+
+ em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ dev->ctl_freq = f->frequency;
+ em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->width;
+ cc->bounds.height = dev->height;
+ cc->defrect = cc->bounds;
+ cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
+ cc->pixelaspect.denominator = 59;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&dev->inqueue))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ if (unlikely(res_get(fh) < 0)) {
+ mutex_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
+ dev->stream = STREAM_ON; /* FIXME: Start video capture here? */
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->stream == STREAM_ON) {
+ em28xx_videodbg("VIDIOC_STREAMOFF: interrupting stream\n");
+ rc = em28xx_stream_interrupt(dev);
+ if (rc < 0) {
+ mutex_unlock(&dev->lock);
+ return rc;
+ }
+ }
+
+ em28xx_empty_framequeues(dev);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+
+ cap->version = EM28XX_VERSION_CODE;
+
+ cap->capabilities =
+ V4L2_CAP_SLICED_VBI_CAPTURE |
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+
+ if (dev->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmtd)
+{
+ if (fmtd->index != 0)
+ return -EINVAL;
+
+ fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(fmtd->description, "Packed YUY2");
+ fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
+ memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
+
+ return 0;
+}
+
+/* Sliced VBI ioctls */
+static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
- if (!list_empty(&dev->outqueue))
- mask |= POLLIN | POLLRDNORM;
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
- mutex_unlock(&dev->fileop_lock);
+ mutex_lock(&dev->lock);
- return mask;
- }
- }
+ f->fmt.sliced.service_set = 0;
- mutex_unlock(&dev->fileop_lock);
- return POLLERR;
-}
+ em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
-/*
- * em28xx_vm_open()
- */
-static void em28xx_vm_open(struct vm_area_struct *vma)
-{
- struct em28xx_frame_t *f = vma->vm_private_data;
- f->vma_use_count++;
+ if (f->fmt.sliced.service_set == 0)
+ rc = -EINVAL;
+
+ mutex_unlock(&dev->lock);
+ return rc;
}
-/*
- * em28xx_vm_close()
- */
-static void em28xx_vm_close(struct vm_area_struct *vma)
+static int vidioc_try_set_vbi_capture(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- /* NOTE: buffers are not freed here */
- struct em28xx_frame_t *f = vma->vm_private_data;
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
- if (f->vma_use_count)
- f->vma_use_count--;
-}
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
-static struct vm_operations_struct em28xx_vm_ops = {
- .open = em28xx_vm_open,
- .close = em28xx_vm_close,
-};
+ mutex_lock(&dev->lock);
+ em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
+ mutex_unlock(&dev->lock);
-/*
- * em28xx_v4l2_mmap()
- */
-static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- unsigned long size = vma->vm_end - vma->vm_start,
- start = vma->vm_start;
- void *pos;
- u32 i;
+ if (f->fmt.sliced.service_set == 0)
+ return -EINVAL;
- struct em28xx *dev = filp->private_data;
+ return 0;
+}
- if (mutex_lock_interruptible(&dev->fileop_lock))
- return -ERESTARTSYS;
- if (dev->state & DEV_DISCONNECTED) {
- em28xx_videodbg("mmap: device not present\n");
- mutex_unlock(&dev->fileop_lock);
- return -ENODEV;
- }
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ u32 i;
+ int rc;
- if (dev->state & DEV_MISCONFIGURED) {
- em28xx_videodbg ("mmap: Device is misconfigured; close and "
- "open it again\n");
- mutex_unlock(&dev->fileop_lock);
- return -EIO;
- }
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
- if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
- size != PAGE_ALIGN(dev->frame[0].buf.length)) {
- mutex_unlock(&dev->fileop_lock);
+ if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
- }
- for (i = 0; i < dev->num_frames; i++) {
- if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
- break;
- }
- if (i == dev->num_frames) {
- em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
- mutex_unlock(&dev->fileop_lock);
+ if (dev->io == IO_READ) {
+ em28xx_videodbg("method is set to read;"
+ " close and open the device again to"
+ " choose the mmap I/O method\n");
return -EINVAL;
}
- /* VM_IO is eventually going to replace PageReserved altogether */
- vma->vm_flags |= VM_IO;
- vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ for (i = 0; i < dev->num_frames; i++)
+ if (dev->frame[i].vma_use_count) {
+ em28xx_videodbg("VIDIOC_REQBUFS failed; "
+ "previous buffers are still mapped\n");
+ return -EINVAL;
+ }
- pos = dev->frame[i].bufmem;
- while (size > 0) { /* size is page-aligned */
- if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
- em28xx_videodbg("mmap: vm_insert_page failed\n");
- mutex_unlock(&dev->fileop_lock);
- return -EAGAIN;
+ mutex_lock(&dev->lock);
+
+ if (dev->stream == STREAM_ON) {
+ em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
+ rc = em28xx_stream_interrupt(dev);
+ if (rc < 0) {
+ mutex_unlock(&dev->lock);
+ return rc;
}
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- size -= PAGE_SIZE;
}
- vma->vm_ops = &em28xx_vm_ops;
- vma->vm_private_data = &dev->frame[i];
+ em28xx_empty_framequeues(dev);
- em28xx_vm_open(vma);
- mutex_unlock(&dev->fileop_lock);
+ em28xx_release_buffers(dev);
+ if (rb->count)
+ rb->count = em28xx_request_buffers(dev, rb->count);
+
+ dev->frame_current = NULL;
+ dev->io = rb->count ? IO_MMAP : IO_NONE;
+
+ mutex_unlock(&dev->lock);
return 0;
}
-/*
- * em28xx_get_ctrl()
- * return the current saturation, brightness or contrast, mute state
- */
-static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
{
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = dev->mute;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = dev->volume;
- return 0;
- default:
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b->index >= dev->num_frames || dev->io != IO_MMAP)
return -EINVAL;
- }
+
+ mutex_lock(&dev->lock);
+
+ memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
+
+ if (dev->frame[b->index].vma_use_count)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (dev->frame[b->index].state == F_DONE)
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ else if (dev->frame[b->index].state != F_UNUSED)
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+
+ mutex_unlock(&dev->lock);
+ return 0;
}
-/*
- * em28xx_set_ctrl()
- * mute or set new saturation, brightness or contrast
- */
-static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value != dev->mute) {
- dev->mute = ctrl->value;
- em28xx_audio_usb_mute(dev, ctrl->value);
- return em28xx_audio_analog_set(dev);
- }
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- dev->volume = ctrl->value;
- return em28xx_audio_analog_set(dev);
- default:
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned long lock_flags;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP ||
+ b->index >= dev->num_frames)
return -EINVAL;
- }
+
+ if (dev->frame[b->index].state != F_UNUSED)
+ return -EAGAIN;
+
+ dev->frame[b->index].state = F_QUEUED;
+
+ /* add frame to fifo */
+ spin_lock_irqsave(&dev->queue_lock, lock_flags);
+ list_add_tail(&dev->frame[b->index].frame, &dev->inqueue);
+ spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+
+ return 0;
}
-/*
- * em28xx_stream_interrupt()
- * stops streaming
- */
-static int em28xx_stream_interrupt(struct em28xx *dev)
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
- int ret = 0;
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+ struct em28xx_frame_t *f;
+ unsigned long lock_flags;
- /* stop reading from the device */
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
- dev->stream = STREAM_INTERRUPT;
- ret = wait_event_timeout(dev->wait_stream,
- (dev->stream == STREAM_OFF) ||
- (dev->state & DEV_DISCONNECTED),
- EM28XX_URB_TIMEOUT);
- if (dev->state & DEV_DISCONNECTED)
- return -ENODEV;
- else if (ret) {
- dev->state |= DEV_MISCONFIGURED;
- em28xx_videodbg("device is misconfigured; close and "
- "open /dev/video%d again\n",
- dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
- return ret;
+ if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&dev->outqueue)) {
+ if (dev->stream == STREAM_OFF)
+ return -EINVAL;
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ rc = wait_event_interruptible(dev->wait_frame,
+ (!list_empty(&dev->outqueue)) ||
+ (dev->state & DEV_DISCONNECTED));
+ if (rc)
+ return rc;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
}
+ spin_lock_irqsave(&dev->queue_lock, lock_flags);
+ f = list_entry(dev->outqueue.next, struct em28xx_frame_t, frame);
+ list_del(dev->outqueue.next);
+ spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+ memcpy(b, &f->buf, sizeof(*b));
+
+ if (f->vma_use_count)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
return 0;
}
-static int em28xx_set_norm(struct em28xx *dev, int width, int height)
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
{
- unsigned int hscale, vscale;
- unsigned int maxh, maxw;
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
- maxw = norm_maxw(dev);
- maxh = norm_maxh(dev);
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
- /* width must even because of the YUYV format */
- /* height must be even because of interlacing */
- height &= 0xfffe;
- width &= 0xfffe;
+ cap->version = EM28XX_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
- if (height < 32)
- height = 32;
- if (height > maxh)
- height = maxh;
- if (width < 48)
- width = 48;
- if (width > maxw)
- width = maxw;
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
- if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
- hscale = 0x3fff;
- width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+ if (unlikely(t->index > 0))
+ return -EINVAL;
- if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
- vscale = 0x3fff;
- height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
- /* set new image size */
- dev->width = width;
- dev->height = height;
- dev->frame_size = dev->width * dev->height * 2;
- dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
- dev->bytesperline = dev->width * 2;
- dev->hscale = hscale;
- dev->vscale = vscale;
+ em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ return 0;
+}
- em28xx_resolution_set(dev);
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
return 0;
}
-static int em28xx_get_fmt(struct em28xx *dev, struct v4l2_format *format)
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
{
- em28xx_videodbg("VIDIOC_G_FMT: type=%s\n",
- (format->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
- "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
- (format->type ==V4L2_BUF_TYPE_VBI_CAPTURE) ?
- "V4L2_BUF_TYPE_VBI_CAPTURE" :
- (format->type ==V4L2_CAP_SLICED_VBI_CAPTURE) ?
- "V4L2_BUF_TYPE_SLICED_VBI_CAPTURE " :
- "not supported");
+ if (unlikely(a->index))
+ return -EINVAL;
- switch (format->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- {
- format->fmt.pix.width = dev->width;
- format->fmt.pix.height = dev->height;
- format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- format->fmt.pix.bytesperline = dev->bytesperline;
- format->fmt.pix.sizeimage = dev->frame_size;
- format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- format->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
+ strcpy(a->name, "Radio");
+ return 0;
+}
- em28xx_videodbg("VIDIOC_G_FMT: %dx%d\n", dev->width,
- dev->height);
- break;
- }
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- {
- format->fmt.sliced.service_set=0;
+ if (0 != t->index)
+ return -EINVAL;
- em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
+ em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
- if (format->fmt.sliced.service_set==0)
- return -EINVAL;
+ return 0;
+}
- break;
- }
+static int radio_s_audio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
- default:
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ if (qc->id < V4L2_CID_BASE ||
+ qc->id >= V4L2_CID_LASTP1)
return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+ if (qc->id && qc->id == em28xx_qctrl[i].id) {
+ memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
}
- return (0);
+
+ return -EINVAL;
}
-static int em28xx_set_fmt(struct em28xx *dev, unsigned int cmd, struct v4l2_format *format)
+/*
+ * em28xx_v4l2_open()
+ * inits the device and starts isoc transfer
+ */
+static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
{
- u32 i;
- int ret = 0;
- int width = format->fmt.pix.width;
- int height = format->fmt.pix.height;
- unsigned int hscale, vscale;
- unsigned int maxh, maxw;
+ int minor = iminor(inode);
+ int errCode = 0, radio = 0;
+ struct em28xx *h,*dev = NULL;
+ struct em28xx_fh *fh;
- maxw = norm_maxw(dev);
- maxh = norm_maxh(dev);
+ list_for_each_entry(h, &em28xx_devlist, devlist) {
+ if (h->vdev->minor == minor) {
+ dev = h;
+ dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ }
+ if (h->vbi_dev->minor == minor) {
+ dev = h;
+ dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ }
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ radio = 1;
+ dev = h;
+ }
+ }
+ if (NULL == dev)
+ return -ENODEV;
+
+ em28xx_videodbg("open minor=%d type=%s users=%d\n",
+ minor,v4l2_type_names[dev->type],dev->users);
+
+ fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
+
+ if (!fh) {
+ em28xx_errdev("em28xx-video.c: Out of memory?!\n");
+ return -ENOMEM;
+ }
+ mutex_lock(&dev->lock);
+ fh->dev = dev;
+ fh->radio = radio;
+ filp->private_data = fh;
+
+ if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+ em28xx_set_alternate(dev);
+
+ dev->width = norm_maxw(dev);
+ dev->height = norm_maxh(dev);
+ dev->frame_size = dev->width * dev->height * 2;
+ dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
+ dev->bytesperline = dev->width * 2;
+ dev->hscale = 0;
+ dev->vscale = 0;
- em28xx_videodbg("%s: type=%s\n",
- cmd == VIDIOC_TRY_FMT ?
- "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
- format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
- "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
- format->type == V4L2_BUF_TYPE_VBI_CAPTURE ?
- "V4L2_BUF_TYPE_VBI_CAPTURE " :
- "not supported");
+ em28xx_capture_start(dev, 1);
+ em28xx_resolution_set(dev);
- if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
- if (format->fmt.sliced.service_set==0)
- return -EINVAL;
+ /* start the transfer */
+ errCode = em28xx_init_isoc(dev);
+ if (errCode)
+ goto err;
- return 0;
+ em28xx_empty_framequeues(dev);
+ }
+ if (fh->radio) {
+ em28xx_videodbg("video_open: setting radio device\n");
+ em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL);
}
+ dev->users++;
- if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- em28xx_videodbg("%s: requested %dx%d\n",
- cmd == VIDIOC_TRY_FMT ?
- "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
- format->fmt.pix.width, format->fmt.pix.height);
+err:
+ mutex_unlock(&dev->lock);
+ return errCode;
+}
- /* FIXME: Move some code away from here */
- /* width must even because of the YUYV format */
- /* height must be even because of interlacing */
- height &= 0xfffe;
- width &= 0xfffe;
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+static void em28xx_release_resources(struct em28xx *dev)
+{
- if (height < 32)
- height = 32;
- if (height > maxh)
- height = maxh;
- if (width < 48)
- width = 48;
- if (width > maxw)
- width = maxw;
+ /*FIXME: I2C IR should be disconnected */
- if(dev->is_em2800){
- /* the em2800 can only scale down to 50% */
- if(height % (maxh / 2))
- height=maxh;
- if(width % (maxw / 2))
- width=maxw;
- /* according to empiatech support */
- /* the MaxPacketSize is to small to support */
- /* framesizes larger than 640x480 @ 30 fps */
- /* or 640x576 @ 25 fps. As this would cut */
- /* of a part of the image we prefer */
- /* 360x576 or 360x480 for now */
- if(width == maxw && height == maxh)
- width /= 2;
+ em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
+ dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
+ dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
+ list_del(&dev->devlist);
+ if (dev->radio_dev) {
+ if (-1 != dev->radio_dev->minor)
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ if (-1 != dev->vbi_dev->minor)
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->vdev) {
+ if (-1 != dev->vdev->minor)
+ video_unregister_device(dev->vdev);
+ else
+ video_device_release(dev->vdev);
+ dev->vdev = NULL;
}
+ em28xx_i2c_unregister(dev);
+ usb_put_dev(dev->udev);
- if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
- hscale = 0x3fff;
+ /* Mark device as unused */
+ em28xx_devused&=~(1<devno);
+}
- width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+/*
+ * em28xx_v4l2_close()
+ * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
+ */
+static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int errCode;
- if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
- vscale = 0x3fff;
+ em28xx_videodbg("users=%d\n", dev->users);
- height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
- format->fmt.pix.width = width;
- format->fmt.pix.height = height;
- format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- format->fmt.pix.bytesperline = width * 2;
- format->fmt.pix.sizeimage = width * 2 * height;
- format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- format->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ if (res_check(fh))
+ res_free(fh);
- em28xx_videodbg("%s: returned %dx%d (%d, %d)\n",
- cmd == VIDIOC_TRY_FMT ?
- "VIDIOC_TRY_FMT" :"VIDIOC_S_FMT",
- format->fmt.pix.width, format->fmt.pix.height, hscale, vscale);
+ mutex_lock(&dev->lock);
- if (cmd == VIDIOC_TRY_FMT)
- return 0;
+ if (dev->users == 1) {
+ em28xx_uninit_isoc(dev);
+ em28xx_release_buffers(dev);
+ dev->io = IO_NONE;
- for (i = 0; i < dev->num_frames; i++)
- if (dev->frame[i].vma_use_count) {
- em28xx_videodbg("VIDIOC_S_FMT failed. "
- "Unmap the buffers first.\n");
- return -EINVAL;
+ /* the device is already disconnect,
+ free the remaining resources */
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_release_resources(dev);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ return 0;
}
- /* stop io in case it is already in progress */
- if (dev->stream == STREAM_ON) {
- em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
- if ((ret = em28xx_stream_interrupt(dev)))
- return ret;
+ /* set alternate 0 */
+ dev->alt = 0;
+ em28xx_videodbg("setting alternate 0\n");
+ errCode = usb_set_interface(dev->udev, 0, 0);
+ if (errCode < 0) {
+ em28xx_errdev("cannot change alternate number to "
+ "0 (error=%i)\n", errCode);
+ }
}
-
- em28xx_release_buffers(dev);
- dev->io = IO_NONE;
-
- /* set new image size */
- dev->width = width;
- dev->height = height;
- dev->frame_size = dev->width * dev->height * 2;
- dev->field_size = dev->frame_size >> 1;
- dev->bytesperline = dev->width * 2;
- dev->hscale = hscale;
- dev->vscale = vscale;
- em28xx_uninit_isoc(dev);
- em28xx_set_alternate(dev);
- em28xx_capture_start(dev, 1);
- em28xx_resolution_set(dev);
- em28xx_init_isoc(dev);
-
+ kfree(fh);
+ dev->users--;
+ wake_up_interruptible_nr(&dev->open, 1);
+ mutex_unlock(&dev->lock);
return 0;
}
/*
- * em28xx_v4l2_do_ioctl()
- * This function is _not_ called directly, but from
- * em28xx_v4l2_ioctl. Userspace
- * copying is done already, arg is a kernel pointer.
+ * em28xx_v4l2_read()
+ * will allocate buffers when called for the first time
*/
-static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
- struct em28xx *dev, unsigned int cmd, void *arg,
- v4l2_kioctl driver_ioctl)
+static ssize_t
+em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
+ loff_t * f_pos)
{
- int ret;
-
- switch (cmd) {
- /* ---------- tv norms ---------- */
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *e = arg;
- unsigned int i;
-
- i = e->index;
- if (i >= TVNORMS)
- return -EINVAL;
- ret = v4l2_video_std_construct(e, tvnorms[e->index].id,
- tvnorms[e->index].name);
- e->index = i;
- if (ret < 0)
- return ret;
- return 0;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
-
- *id = dev->tvnorm->id;
- return 0;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- unsigned int i;
+ struct em28xx_frame_t *f, *i;
+ unsigned long lock_flags;
+ int ret = 0;
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
- for (i = 0; i < TVNORMS; i++)
- if (*id == tvnorms[i].id)
- break;
- if (i == TVNORMS)
- for (i = 0; i < TVNORMS; i++)
- if (*id & tvnorms[i].id)
- break;
- if (i == TVNORMS)
- return -EINVAL;
+ /* FIXME: read() is not prepared to allow changing the video
+ resolution while streaming. Seems a bug at em28xx_set_fmt
+ */
- mutex_lock(&dev->lock);
- dev->tvnorm = &tvnorms[i];
+ if (unlikely(res_get(fh) < 0))
+ return -EBUSY;
- em28xx_set_norm(dev, dev->width, dev->height);
+ mutex_lock(&dev->lock);
- em28xx_i2c_call_clients(dev, VIDIOC_S_STD,
- &dev->tvnorm->id);
+ if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
+ if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
+ em28xx_videodbg("not supported yet! ...\n");
+ if (copy_to_user(buf, "", 1)) {
+ mutex_unlock(&dev->lock);
+ return -EFAULT;
+ }
mutex_unlock(&dev->lock);
-
- return 0;
+ return (1);
}
-
- /* ------ input switching ---------- */
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
- unsigned int n;
- static const char *iname[] = {
- [EM28XX_VMUX_COMPOSITE1] = "Composite1",
- [EM28XX_VMUX_COMPOSITE2] = "Composite2",
- [EM28XX_VMUX_COMPOSITE3] = "Composite3",
- [EM28XX_VMUX_COMPOSITE4] = "Composite4",
- [EM28XX_VMUX_SVIDEO] = "S-Video",
- [EM28XX_VMUX_TELEVISION] = "Television",
- [EM28XX_VMUX_CABLE] = "Cable TV",
- [EM28XX_VMUX_DVB] = "DVB",
- [EM28XX_VMUX_DEBUG] = "for debug only",
- };
-
- n = i->index;
- if (n >= MAX_EM28XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(n)->type)
- return -EINVAL;
- memset(i, 0, sizeof(*i));
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name, iname[INPUT(n)->type]);
- if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
- (EM28XX_VMUX_CABLE == INPUT(n)->type))
- i->type = V4L2_INPUT_TYPE_TUNER;
- for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
- i->std |= tvnorms[n].id;
- return 0;
+ if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+ em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n");
+ em28xx_videodbg("not supported yet! ...\n");
+ if (copy_to_user(buf, "", 1)) {
+ mutex_unlock(&dev->lock);
+ return -EFAULT;
+ }
+ mutex_unlock(&dev->lock);
+ return (1);
}
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = dev->ctl_input;
- return 0;
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_videodbg("device not present\n");
+ mutex_unlock(&dev->lock);
+ return -ENODEV;
}
- case VIDIOC_S_INPUT:
- {
- int *index = arg;
-
- if (*index >= MAX_EM28XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(*index)->type)
- return -EINVAL;
- mutex_lock(&dev->lock);
- video_mux(dev, *index);
+ if (dev->state & DEV_MISCONFIGURED) {
+ em28xx_videodbg("device misconfigured; close and open it again\n");
mutex_unlock(&dev->lock);
-
- return 0;
+ return -EIO;
}
- case VIDIOC_G_AUDIO:
- {
- struct v4l2_audio *a = arg;
- unsigned int index = a->index;
- if (a->index > 1)
- return -EINVAL;
- memset(a, 0, sizeof(*a));
- index = dev->ctl_ainput;
+ if (dev->io == IO_MMAP) {
+ em28xx_videodbg ("IO method is set to mmap; close and open"
+ " the device again to choose the read method\n");
+ mutex_unlock(&dev->lock);
+ return -EINVAL;
+ }
- if (index == 0) {
- strcpy(a->name, "Television");
- } else {
- strcpy(a->name, "Line In");
+ if (dev->io == IO_NONE) {
+ if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
+ em28xx_errdev("read failed, not enough memory\n");
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
}
- a->capability = V4L2_AUDCAP_STEREO;
- a->index = index;
- return 0;
+ dev->io = IO_READ;
+ dev->stream = STREAM_ON;
+ em28xx_queue_unusedframes(dev);
}
- case VIDIOC_S_AUDIO:
- {
- struct v4l2_audio *a = arg;
-
- if (a->index != dev->ctl_ainput)
- return -EINVAL;
+ if (!count) {
+ mutex_unlock(&dev->lock);
return 0;
}
- /* --- controls ---------------------------------------------- */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i, id=qc->id;
-
- memset(qc,0,sizeof(*qc));
- qc->id=id;
-
- if (!dev->has_msp34xx) {
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (qc->id && qc->id == em28xx_qctrl[i].id) {
- memcpy(qc, &(em28xx_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
- }
+ if (list_empty(&dev->outqueue)) {
+ if (filp->f_flags & O_NONBLOCK) {
+ mutex_unlock(&dev->lock);
+ return -EAGAIN;
}
- em28xx_i2c_call_clients(dev,cmd,qc);
- if (qc->type)
- return 0;
- else
- return -EINVAL;
+ ret = wait_event_interruptible
+ (dev->wait_frame,
+ (!list_empty(&dev->outqueue)) ||
+ (dev->state & DEV_DISCONNECTED));
+ if (ret) {
+ mutex_unlock(&dev->lock);
+ return ret;
+ }
+ if (dev->state & DEV_DISCONNECTED) {
+ mutex_unlock(&dev->lock);
+ return -ENODEV;
+ }
+ dev->video_bytesread = 0;
}
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- int retval=-EINVAL;
- if (!dev->has_msp34xx)
- retval=em28xx_get_ctrl(dev, ctrl);
- if (retval==-EINVAL) {
- em28xx_i2c_call_clients(dev,cmd,arg);
- return 0;
- } else return retval;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- u8 i;
-
- if (!dev->has_msp34xx){
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (ctrl->id == em28xx_qctrl[i].id) {
- if (ctrl->value <
- em28xx_qctrl[i].minimum
- || ctrl->value >
- em28xx_qctrl[i].maximum)
- return -ERANGE;
- return em28xx_set_ctrl(dev, ctrl);
- }
- }
- }
+ f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame);
- em28xx_i2c_call_clients(dev,cmd,arg);
- return 0;
- }
- /* --- tuner ioctls ------------------------------------------ */
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *t = arg;
+ em28xx_queue_unusedframes(dev);
- if (0 != t->index)
- return -EINVAL;
+ if (count > f->buf.length)
+ count = f->buf.length;
- memset(t, 0, sizeof(*t));
- strcpy(t->name, "Tuner");
- mutex_lock(&dev->lock);
- /* let clients fill in the remainder of this struct */
- em28xx_i2c_call_clients(dev, cmd, t);
- mutex_unlock(&dev->lock);
- em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal,
- t->afc);
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *t = arg;
+ if ((dev->video_bytesread + count) > dev->frame_size)
+ count = dev->frame_size - dev->video_bytesread;
- if (0 != t->index)
- return -EINVAL;
- mutex_lock(&dev->lock);
- /* let clients handle this */
- em28xx_i2c_call_clients(dev, cmd, t);
- mutex_unlock(&dev->lock);
- return 0;
+ if (copy_to_user(buf, f->bufmem+dev->video_bytesread, count)) {
+ em28xx_err("Error while copying to user\n");
+ return -EFAULT;
}
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ dev->video_bytesread += count;
- memset(f, 0, sizeof(*f));
- f->type = V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->ctl_freq;
+ if (dev->video_bytesread == dev->frame_size) {
+ spin_lock_irqsave(&dev->queue_lock, lock_flags);
+ list_for_each_entry(i, &dev->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&dev->outqueue);
+ spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
- return 0;
+ em28xx_queue_unusedframes(dev);
+ dev->video_bytesread = 0;
}
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
- if (0 != f->tuner)
- return -EINVAL;
+ *f_pos += count;
- if (V4L2_TUNER_ANALOG_TV != f->type)
- return -EINVAL;
+ mutex_unlock(&dev->lock);
- mutex_lock(&dev->lock);
- dev->ctl_freq = f->frequency;
- em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
- mutex_unlock(&dev->lock);
- return 0;
- }
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *cc = arg;
+ return count;
+}
- if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- cc->bounds.left = 0;
- cc->bounds.top = 0;
- cc->bounds.width = dev->width;
- cc->bounds.height = dev->height;
- cc->defrect = cc->bounds;
- cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
- cc->pixelaspect.denominator = 59;
- return 0;
- }
- case VIDIOC_STREAMON:
- {
- int *type = arg;
+/*
+ * em28xx_v4l2_poll()
+ * will allocate buffers when called for the first time
+ */
+static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
+{
+ unsigned int mask = 0;
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
- if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || dev->io != IO_MMAP)
- return -EINVAL;
+ if (unlikely(res_get(fh) < 0))
+ return POLLERR;
- if (list_empty(&dev->inqueue))
- return -EINVAL;
+ mutex_lock(&dev->lock);
- dev->stream = STREAM_ON; /* FIXME: Start video capture here? */
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_videodbg("device not present\n");
+ } else if (dev->state & DEV_MISCONFIGURED) {
+ em28xx_videodbg("device is misconfigured; close and open it again\n");
+ } else {
+ if (dev->io == IO_NONE) {
+ if (!em28xx_request_buffers
+ (dev, EM28XX_NUM_READ_FRAMES)) {
+ em28xx_warn
+ ("poll() failed, not enough memory\n");
+ } else {
+ dev->io = IO_READ;
+ dev->stream = STREAM_ON;
+ }
+ }
- em28xx_videodbg("VIDIOC_STREAMON: starting stream\n");
+ if (dev->io == IO_READ) {
+ em28xx_queue_unusedframes(dev);
+ poll_wait(filp, &dev->wait_frame, wait);
- return 0;
- }
- case VIDIOC_STREAMOFF:
- {
- int *type = arg;
- int ret;
+ if (!list_empty(&dev->outqueue))
+ mask |= POLLIN | POLLRDNORM;
- if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || dev->io != IO_MMAP)
- return -EINVAL;
+ mutex_unlock(&dev->lock);
- if (dev->stream == STREAM_ON) {
- em28xx_videodbg ("VIDIOC_STREAMOFF: interrupting stream\n");
- if ((ret = em28xx_stream_interrupt(dev)))
- return ret;
+ return mask;
}
- em28xx_empty_framequeues(dev);
-
- return 0;
}
- default:
- return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
- driver_ioctl);
- }
- return 0;
+
+ mutex_unlock(&dev->lock);
+ return POLLERR;
}
/*
- * em28xx_v4l2_do_ioctl()
- * This function is _not_ called directly, but from
- * em28xx_v4l2_ioctl. Userspace
- * copying is done already, arg is a kernel pointer.
+ * em28xx_v4l2_mmap()
*/
-static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, void *arg)
+static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
{
- struct em28xx *dev = filp->private_data;
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ if (unlikely(res_get(fh) < 0))
+ return -EBUSY;
- if (!dev)
- return -ENODEV;
+ mutex_lock(&dev->lock);
- if (video_debug > 1)
- v4l_print_ioctl(dev->name,cmd);
-
- switch (cmd) {
-
- /* --- capabilities ------------------------------------------ */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
-
- memset(cap, 0, sizeof(*cap));
- strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
- strlcpy(cap->card, em28xx_boards[dev->model].name,
- sizeof(cap->card));
- strlcpy(cap->bus_info, dev->udev->dev.bus_id,
- sizeof(cap->bus_info));
- cap->version = EM28XX_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_SLICED_VBI_CAPTURE |
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
- if (dev->has_tuner)
- cap->capabilities |= V4L2_CAP_TUNER;
- return 0;
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_videodbg("mmap: device not present\n");
+ mutex_unlock(&dev->lock);
+ return -ENODEV;
}
- /* --- capture ioctls ---------------------------------------- */
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *fmtd = arg;
- if (fmtd->index != 0)
- return -EINVAL;
- memset(fmtd, 0, sizeof(*fmtd));
- fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmtd->description, "Packed YUY2");
- fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
- memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
- return 0;
+ if (dev->state & DEV_MISCONFIGURED) {
+ em28xx_videodbg ("mmap: Device is misconfigured; close and "
+ "open it again\n");
+ mutex_unlock(&dev->lock);
+ return -EIO;
}
- case VIDIOC_G_FMT:
- return em28xx_get_fmt(dev, (struct v4l2_format *) arg);
-
- case VIDIOC_TRY_FMT:
- case VIDIOC_S_FMT:
- return em28xx_set_fmt(dev, cmd, (struct v4l2_format *)arg);
- case VIDIOC_REQBUFS:
- {
- struct v4l2_requestbuffers *rb = arg;
- u32 i;
- int ret;
+ if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE)) {
+ mutex_unlock(&dev->lock);
+ return -EINVAL;
+ }
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- rb->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
+ if (size > PAGE_ALIGN(dev->frame[0].buf.length))
+ size = PAGE_ALIGN(dev->frame[0].buf.length);
- if (dev->io == IO_READ) {
- em28xx_videodbg ("method is set to read;"
- " close and open the device again to"
- " choose the mmap I/O method\n");
- return -EINVAL;
- }
+ for (i = 0; i < dev->num_frames; i++) {
+ if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == dev->num_frames) {
+ em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
+ mutex_unlock(&dev->lock);
+ return -EINVAL;
+ }
- for (i = 0; i < dev->num_frames; i++)
- if (dev->frame[i].vma_use_count) {
- em28xx_videodbg ("VIDIOC_REQBUFS failed; previous buffers are still mapped\n");
- return -EINVAL;
- }
+ /* VM_IO is eventually going to replace PageReserved altogether */
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
- if (dev->stream == STREAM_ON) {
- em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
- if ((ret = em28xx_stream_interrupt(dev)))
- return ret;
+ pos = dev->frame[i].bufmem;
+ while (size > 0) { /* size is page-aligned */
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ em28xx_videodbg("mmap: vm_insert_page failed\n");
+ mutex_unlock(&dev->lock);
+ return -EAGAIN;
}
-
- em28xx_empty_framequeues(dev);
-
- em28xx_release_buffers(dev);
- if (rb->count)
- rb->count =
- em28xx_request_buffers(dev, rb->count);
-
- dev->frame_current = NULL;
-
- em28xx_videodbg ("VIDIOC_REQBUFS: setting io method to mmap: num bufs %i\n",
- rb->count);
- dev->io = rb->count ? IO_MMAP : IO_NONE;
- return 0;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
}
- case VIDIOC_QUERYBUF:
- {
- struct v4l2_buffer *b = arg;
- if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- b->index >= dev->num_frames || dev->io != IO_MMAP)
- return -EINVAL;
+ vma->vm_ops = &em28xx_vm_ops;
+ vma->vm_private_data = &dev->frame[i];
- memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
+ em28xx_vm_open(vma);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
- if (dev->frame[b->index].vma_use_count) {
- b->flags |= V4L2_BUF_FLAG_MAPPED;
- }
- if (dev->frame[b->index].state == F_DONE)
- b->flags |= V4L2_BUF_FLAG_DONE;
- else if (dev->frame[b->index].state != F_UNUSED)
- b->flags |= V4L2_BUF_FLAG_QUEUED;
- return 0;
- }
- case VIDIOC_QBUF:
- {
- struct v4l2_buffer *b = arg;
- unsigned long lock_flags;
+static const struct file_operations em28xx_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .read = em28xx_v4l2_read,
+ .poll = em28xx_v4l2_poll,
+ .mmap = em28xx_v4l2_mmap,
+ .ioctl = video_ioctl2,
+ .llseek = no_llseek,
+ .compat_ioctl = v4l_compat_ioctl32,
+};
- if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- b->index >= dev->num_frames || dev->io != IO_MMAP) {
- return -EINVAL;
- }
+static const struct file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
- if (dev->frame[b->index].state != F_UNUSED) {
- return -EAGAIN;
- }
- dev->frame[b->index].state = F_QUEUED;
+static const struct video_device em28xx_video_template = {
+ .fops = &em28xx_v4l_fops,
+ .release = video_device_release,
+
+ .minor = -1,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
+ .vidioc_g_fmt_cap = vidioc_g_fmt_cap,
+ .vidioc_try_fmt_cap = vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = vidioc_s_fmt_cap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+
+ .vidioc_g_fmt_vbi_capture = vidioc_g_fmt_vbi_capture,
+ .vidioc_try_fmt_vbi_capture = vidioc_try_set_vbi_capture,
+ .vidioc_s_fmt_vbi_capture = vidioc_try_set_vbi_capture,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+
+ .tvnorms = V4L2_STD_ALL,
+ .current_norm = V4L2_STD_PAL,
+};
- /* add frame to fifo */
- spin_lock_irqsave(&dev->queue_lock, lock_flags);
- list_add_tail(&dev->frame[b->index].frame,
- &dev->inqueue);
- spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+static struct video_device em28xx_radio_template = {
+ .name = "em28xx-radio",
+ .type = VID_TYPE_TUNER,
+ .fops = &radio_fops,
+ .minor = -1,
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+};
- return 0;
- }
- case VIDIOC_DQBUF:
- {
- struct v4l2_buffer *b = arg;
- struct em28xx_frame_t *f;
- unsigned long lock_flags;
- int ret = 0;
+/******************************** usb interface *****************************************/
- if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || dev->io != IO_MMAP)
- return -EINVAL;
- if (list_empty(&dev->outqueue)) {
- if (dev->stream == STREAM_OFF)
- return -EINVAL;
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- ret = wait_event_interruptible
- (dev->wait_frame,
- (!list_empty(&dev->outqueue)) ||
- (dev->state & DEV_DISCONNECTED));
- if (ret)
- return ret;
- if (dev->state & DEV_DISCONNECTED)
- return -ENODEV;
- }
+static LIST_HEAD(em28xx_extension_devlist);
+static DEFINE_MUTEX(em28xx_extension_devlist_lock);
- spin_lock_irqsave(&dev->queue_lock, lock_flags);
- f = list_entry(dev->outqueue.next,
- struct em28xx_frame_t, frame);
- list_del(dev->outqueue.next);
- spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+int em28xx_register_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *h, *dev = NULL;
- f->state = F_UNUSED;
- memcpy(b, &f->buf, sizeof(*b));
+ list_for_each_entry(h, &em28xx_devlist, devlist)
+ dev = h;
- if (f->vma_use_count)
- b->flags |= V4L2_BUF_FLAG_MAPPED;
+ mutex_lock(&em28xx_extension_devlist_lock);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ if (dev)
+ ops->init(dev);
+
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+ mutex_unlock(&em28xx_extension_devlist_lock);
- return 0;
- }
- default:
- return em28xx_do_ioctl(inode, filp, dev, cmd, arg,
- em28xx_video_do_ioctl);
- }
return 0;
}
+EXPORT_SYMBOL(em28xx_register_extension);
-/*
- * em28xx_v4l2_ioctl()
- * handle v4l2 ioctl the main action happens in em28xx_v4l2_do_ioctl()
- */
-static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+void em28xx_unregister_extension(struct em28xx_ops *ops)
{
- int ret = 0;
- struct em28xx *dev = filp->private_data;
+ struct em28xx *h, *dev = NULL;
- if (mutex_lock_interruptible(&dev->fileop_lock))
- return -ERESTARTSYS;
+ list_for_each_entry(h, &em28xx_devlist, devlist)
+ dev = h;
- if (dev->state & DEV_DISCONNECTED) {
- em28xx_errdev("v4l2 ioctl: device not present\n");
- mutex_unlock(&dev->fileop_lock);
- return -ENODEV;
- }
-
- if (dev->state & DEV_MISCONFIGURED) {
- em28xx_errdev
- ("v4l2 ioctl: device is misconfigured; close and open it again\n");
- mutex_unlock(&dev->fileop_lock);
- return -EIO;
- }
+ if (dev)
+ ops->fini(dev);
- ret = video_usercopy(inode, filp, cmd, arg, em28xx_video_do_ioctl);
+ mutex_lock(&em28xx_extension_devlist_lock);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+}
+EXPORT_SYMBOL(em28xx_unregister_extension);
- mutex_unlock(&dev->fileop_lock);
+struct video_device *em28xx_vdev_init(struct em28xx *dev,
+ const struct video_device *template,
+ const int type,
+ const char *type_name)
+{
+ struct video_device *vfd;
- return ret;
-}
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+ vfd->dev = &dev->udev->dev;
+ vfd->release = video_device_release;
+ vfd->type = type;
-static const struct file_operations em28xx_v4l_fops = {
- .owner = THIS_MODULE,
- .open = em28xx_v4l2_open,
- .release = em28xx_v4l2_close,
- .ioctl = em28xx_v4l2_ioctl,
- .read = em28xx_v4l2_read,
- .poll = em28xx_v4l2_poll,
- .mmap = em28xx_v4l2_mmap,
- .llseek = no_llseek,
- .compat_ioctl = v4l_compat_ioctl32,
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s",
+ dev->name, type_name);
-};
+ return vfd;
+}
-/******************************** usb interface *****************************************/
/*
* em28xx_init_dev()
* allocates and inits the device structs, registers i2c bus and v4l device
*/
static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
- int minor, int model)
+ int minor)
{
+ struct em28xx_ops *ops = NULL;
struct em28xx *dev = *devhandle;
int retval = -ENOMEM;
- int errCode, i;
+ int errCode;
unsigned int maxh, maxw;
dev->udev = udev;
- dev->model = model;
mutex_init(&dev->lock);
+ spin_lock_init(&dev->queue_lock);
init_waitqueue_head(&dev->open);
+ init_waitqueue_head(&dev->wait_frame);
+ init_waitqueue_head(&dev->wait_stream);
dev->em28xx_write_regs = em28xx_write_regs;
dev->em28xx_read_reg = em28xx_read_reg;
dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
dev->em28xx_write_regs_req = em28xx_write_regs_req;
dev->em28xx_read_reg_req = em28xx_read_reg_req;
- dev->is_em2800 = em28xx_boards[model].is_em2800;
- dev->has_tuner = em28xx_boards[model].has_tuner;
- dev->has_msp34xx = em28xx_boards[model].has_msp34xx;
- dev->tda9887_conf = em28xx_boards[model].tda9887_conf;
- dev->decoder = em28xx_boards[model].decoder;
-
- if (tuner >= 0)
- dev->tuner_type = tuner;
- else
- dev->tuner_type = em28xx_boards[model].tuner_type;
+ dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
- dev->video_inputs = em28xx_boards[model].vchannels;
+ errCode = em28xx_read_reg(dev, CHIPID_REG);
+ if (errCode >= 0)
+ em28xx_info("em28xx chip ID = %d\n", errCode);
- for (i = 0; i < TVNORMS; i++)
- if (em28xx_boards[model].norm == tvnorms[i].mode)
- break;
- if (i == TVNORMS)
- i = 0;
+ em28xx_pre_card_setup(dev);
+
+ errCode = em28xx_config(dev);
+ if (errCode) {
+ em28xx_errdev("error configuring device\n");
+ em28xx_devused &= ~(1<devno);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ /* register i2c bus */
+ em28xx_i2c_register(dev);
- dev->tvnorm = &tvnorms[i]; /* set default norm */
+ /* Do board specific init and eeprom reading */
+ em28xx_card_setup(dev);
+
+ /* Configure audio */
+ em28xx_audio_analog_set(dev);
- em28xx_videodbg("tvnorm=%s\n", dev->tvnorm->name);
+ /* configure the device */
+ em28xx_config_i2c(dev);
+
+ /* set default norm */
+ dev->norm = em28xx_video_template.current_norm;
maxw = norm_maxw(dev);
maxh = norm_maxh(dev);
@@ -1555,138 +1890,110 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->vscale = 0;
dev->ctl_input = 2;
- /* setup video picture settings for saa7113h */
- memset(&dev->vpic, 0, sizeof(dev->vpic));
- dev->vpic.colour = 128 << 8;
- dev->vpic.hue = 128 << 8;
- dev->vpic.brightness = 128 << 8;
- dev->vpic.contrast = 192 << 8;
- dev->vpic.whiteness = 128 << 8; /* This one isn't used */
- dev->vpic.depth = 16;
- dev->vpic.palette = VIDEO_PALETTE_YUV422;
-
- em28xx_pre_card_setup(dev);
-#ifdef CONFIG_MODULES
- /* request some modules */
- if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
- request_module("saa7115");
- if (dev->decoder == EM28XX_TVP5150)
- request_module("tvp5150");
- if (dev->has_tuner)
- request_module("tuner");
-#endif
errCode = em28xx_config(dev);
- if (errCode) {
- em28xx_errdev("error configuring device\n");
- em28xx_devused&=~(1<devno);
- kfree(dev);
- return -ENOMEM;
- }
-
- mutex_lock(&dev->lock);
- /* register i2c bus */
- em28xx_i2c_register(dev);
-
- /* Do board specific init and eeprom reading */
- em28xx_card_setup(dev);
-
- /* configure the device */
- em28xx_config_i2c(dev);
-
- mutex_unlock(&dev->lock);
- errCode = em28xx_config(dev);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
-#ifdef CONFIG_MODULES
- if (dev->has_msp34xx)
- request_module("msp3400");
-#endif
- /* allocate and fill v4l2 device struct */
- dev->vdev = video_device_alloc();
+ /* allocate and fill video video_device struct */
+ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template,
+ VID_TYPE_CAPTURE, "video");
if (NULL == dev->vdev) {
em28xx_errdev("cannot allocate video_device.\n");
- em28xx_devused&=~(1<